I want to inject a MenuStrip Item or create a new one if it doesn't exists (but this is a different question) from the base constructor, the logic is the following:
public class LocalizedForm : Form
{
public LocalizedForm()
{
Shown += (sender, e) =>
{
MenuStrip menu = null;
if (HasValidMenu(out menu))
LanguageManager.AttachMenu(menu); //Language Manager will inject MenuStripItems to the passed MenuStrip
};
}
protected bool HasValidMenu(out MenuStrip menu)
{
try
{
menu = Controls.OfType<MenuStrip>().SingleOrDefault(x => x.Dock == DockStyle.Top);
return menu != null;
}
catch
{
menu = null;
return false;
}
}
}
and two childs:
public partial class frmMain : LocalizedForm
{
}
public partial class frmCredentials : LocalizedForm
{
}
Why I use Shown event? That's because the MenuStrip will not be found in the Controls until it is rendered or loaded.
Well, my main problem now is that the menu options are created twice instead one time.
As you can see there:
By this reason I have changed the following things:
protected bool HasValidMenu(out MenuStrip menu) to protected bool HasValidMenu(string name, out MenuStrip menu)
The call has been changed to:
if (HasValidMenu(out menu)) to if (HasValidMenu(GetType().Name, out menu))
This will return the main type name from where this was called.
And the magic:
menu = Controls.OfType<MenuStrip>().SingleOrDefault(x => x.Dock == DockStyle.Top); to
menu = Application.OpenForms[name].Controls.OfType<MenuStrip>().SingleOrDefault(x => x.Dock == DockStyle.Top);
But for some reason, Shown event doesn't affect to derived classes. I have testing by calling the Load or Shown event of derived classes by using Application.OpenForms[name] but this is also null.
So, what can I do?
Related
I have written a user control, MenuItem, which inherits from a Form Label.
I have a backgroundworker thread whose IsBusy property is exposed through a property in the MainForm as IsBackgroundBusy.
How do I read this property from the MenuItem usercontrol? I am currently using Application.UseWaitCursor and I set that in the backgroundworker and it works perfectly, however I do not want the cursor to change. That's why I figured a property that I could set would be much better.
Here is the code in my MainForm:
public partial class MainForm : Form
{
public bool IsBackgroundBusy
{
get
{
return bwRefreshGalleries.IsBusy;
}
}
Here is the code for my usercontrol:
public partial class MenuItem: Label
{
private bool _disableIfBusy = false;
[Description("Change color if Application.UseWaitCursor is True")]
public bool DisableIfBusy
{
get
{
return _disableIfBusy;
}
set
{
_disableIfBusy = value;
}
}
public MenuItem()
{
InitializeComponent();
}
protected override void OnMouseEnter( EventArgs e )
{
if ( Application.UseWaitCursor && _disableIfBusy )
{
this.BackColor = SystemColors.ControlDark;
}
else
{
this.BackColor = SystemColors.Control;
}
base.OnMouseEnter( e );
}
(Note: it's not clear to me whether you have an actual UserControl here or not. The MenuItem class you show inherits Label, not UserControl. You should probably avoid using the term "usercontrol" or "user control" when you are not actually dealing with a UserControl object).
Absent a complete code example, it's hard to know exactly what the right solution here is. However, assuming you are using the BackgroundWorker in a typical fashion, then you simply need for the owner of the control (i.e. the containing Form) to pass the necessary state to the control as it changes. E.g.:
class MenuItem : Label
{
public bool IsParentBusy { get; set; }
}
// I.e. some method where you are handling the BackgroundWorker
void button1_Click(object sender, EventArgs e)
{
// ...some other initialization...
bwRefreshGalleries.RunWorkerCompleted += (sender1, e1) =>
{
menuItem1.IsParentBusy = false;
};
menuItem1.ParentIsBusy = true;
bwRefreshGalleries.RunAsync();
}
If you already have a handler for the RunWorkerCompleted event, then just put the statement to set the IsParentBusy property there instead of adding another handler.
Then instead of using the Application.UseWaitCursor property, you can just look at the IsParentBusy property.
There are other mechanisms you could use; I do agree with the general sentiment that the MenuItem control should not be tied to your specific Form sub-class. If for some reason the above doesn't work in your case, you need to elaborate on your question: provide a good code example and explain exactly why simply having the container of the control manage its state directly doesn't work for you
So in my program I have a settings page. On the settings page, there is an option to set the program "Always on Top". When this option is checked and unchecked, it properly saves the setting, but it does not actually change TopMost property itself.
The program's main form is called "MainForm", but the settings page is called "SettingsForm". How would I change the "TopMost" property on "MainForm", from within the "SettingsForm"?
You could create an event on Settings form:
public event EventHandler TopMostEvent;
private void OnTopMostEvent()
{
if (TopMostEvent != null)
{
TopMostEvent(this, EventArgs.Empty);
}
}
On CheckedChanged event call the method after saving settings:
OnTopMostEvent();
And in Main form subscribe to the event and set the forms TopMost property
One approach would be to simply give SettingForm a reference to MainForm, e.g. via a constructor parameter which is then stored to a field where it can later be accessed when necessary.
For example:
public class SettingsForm
{
public SettingsForm(MainForm mainForm)
{
this.mainForm = mainForm;
}
public void Apple()
{
this.mainForm.TopMost = true;
}
private readonly MainForm mainForm;
}
public class MainForm
{
public void Banana()
{
var settingsForm = new SettingsForm(this);
settingsForm.ShowDialog();
}
}
(However, it may not be necessary to do this if the owner of SettingsForm is already the insntance of MainForm but this I cannot tell from what you have given.)
This is a good place for a mediator pattern. (Similar to a controller) The idea is you have one object that creates all of your windows and passes a reference to itself into each form through the constructor. You can call a method in the mediator from either form and the mediator will focus the MainForm. It's a very common practice in Windows Forms.
So you'll make a mediator class like so:
public class MyMediator
{
Form mainForm {get;set;}
Form settingsForm{get;set;}
public MyMediator()
{
mainForm = new MainForm(this);
mainForm.Show();
}
...
public FocusMainForm() // call this from settings form
{
mainForm.TopMost = true;
}
}
I have a mydatagridview class which inherits from the built-in DataGridView control, as shown below:
public class mydatagridview : DataGridView
{
protected override bool ProcessDataGridViewKey(KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
this.ProcessTabKey(e.KeyData);
return true;
}
return base.ProcessDataGridViewKey(e);
}
protected override bool ProcessDialogKey(Keys keyData)
{
if (keyData == Keys.Enter)
{
this.ProcessTabKey(keyData);
return true;
}
return base.ProcessDialogKey(keyData);
}
}
Now I want to utilize it in my main class:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
I want to Utilize myDatagridview with Datagridview1 of : public partial class Form1 : Form
How can I do this?
You need to create an instance of your custom control class, and then add that instance to your form's Controls collection. For example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Create an instance of your custom control
mydatagridview myDGV = new mydatagridview();
// Add that instance to your form's Controls collection
this.Controls.Add(myDGV);
}
}
Of course, you could also do the same thing from the Designer. It will automatically insert code very similar to that shown above inside the InitializeComponent() method.
If your custom control doesn't show up in the Toolbox automatically after you've rebuilt your project, make sure that you've enabled toolbox auto-population:
From the "Tools" menu, select "Options".
Expand the "Windows Forms Designer" category.
Set the "AutoToolboxPopulate" property to True.
If I understand correctly, and i'm not sure that I do, you can just use it like any other type:
mydatagridview mydatagrid = new mydatagridview();
this.Controls.Add(mydatagrid);
Next to the answers that have already been given, it should be possible to drag and the drop the control from the toolbox to your form.
If you create a user control, or a custom Control, and build your project, the control should show up in the toolbox.
Just trying to figure out an easy way to either pass or share some data between the main window and a dialog box.
I've got a collection of variables in my main window that I want to pass to a dialog box so that they can be edited.
The way I've done it now, is I pass in the list to the constructor of the dialog box:
private void Button_Click(object sender, RoutedEventArgs e)
{
var window = new VariablesWindow(_templateVariables);
window.Owner = this;
window.ShowDialog();
if(window.DialogResult == true)
_templateVariables = new List<Variable>(window.Variables);
}
And then in there, I guess I need to deep-copy the list,
public partial class VariablesWindow : Window
{
public ObservableCollection<Variable> Variables { get; set; }
public VariablesWindow(IEnumerable<Variable> vars)
{
Variables = new ObservableCollection<Variable>(vars);
// ...
So that when they're edited, it doesn't get reflected back in the main window until the user actually hits "Save".
Is that the correct approach? If so, is there an easy way to deep-copy an ObservableCollection? Because as it stands now, I think my Variables are being modified because it's only doing a shallow-copy.
I think you are indeed following the right approach here, but you need to make a deep copy of your ObservableCollection. To do so, make sure that your class 'Variable' is Clonable (try to implement ICloneable)
foreach(var item in vars)
{
Variables.Add((Variable)item.Clone());
}
I would use events to communicate between the two forms if you want the main form to update while the dialog is open. Expose an event ("ItemAdded" or whatever) from your dialog class that the main form can handle. When that event is fired, update the main form as needed.
This extension method might help somebody:
public static IEnumerable<T> DeepCopy<T>(this IEnumerable<T> collection) where T : ICloneable
{
return collection.Select(x => (T) x.Clone());
}
It simplifies my dialog window slightly:
public partial class VariablesWindow : Window
{
public ObservableCollection<TemplateVariable> Variables { get; private set; }
public VariablesWindow(IEnumerable<TemplateVariable> vars)
{
Variables = new ObservableCollection<TemplateVariable>(vars.DeepCopy());
I've got an ASP-UserControl QuestionWithAnswer (.ascx) : BaseQuestion : UserControl
and a ControlDesigner QuestionDesigner : UserControlDesigner.
Now i use the DesignerAttribute to associate control and designer:
[Designer(typeof(QuestionDesigner))]
public class BaseQuestion : UserControl
all types are in the same assembly (WEB Application).
But it still loads UserControlDesigner instead of mine.
Did i have to put my designer in a seperate assembly?
I suppose the asp-page designer cannot find the designer.
thx!
mo
demo code:
public class FragenDesigner : UserControlDesigner
{
private DesignerActionList _actionList;
private DesignerVerb[] _verbs;
public override DesignerActionListCollection ActionLists
{
get
{
if (_actionList == null)
{
_actionList = new DesignerActionList(new System.Windows.Forms.TextBox());
_actionList.AutoShow = true;
ActionLists.Add(_actionList);
}
return base.ActionLists;
}
}
public override DesignerVerbCollection Verbs
{
get
{
if (_verbs == null)
{
_verbs = new DesignerVerb[]
{
new DesignerVerb("test", onblabla),
};
Verbs.AddRange(_verbs);
}
return base.Verbs;
}
}
private void onblabla(object sender, EventArgs e)
{
MessageBox.Show("blabla");
}
}
okay there is already an answer: http://msdn.microsoft.com/en-us/library/system.web.ui.design.usercontroldesigner.aspx.
Remarks
There is no developer advantage to
creating your own designer derived
from UserControlDesigner. To enhance
the design-time experience for a
custom control, derive your control
from CompositeControl and your
designer from
CompositeControlDesigner. In that
case, you would not use an .ascx file
for your ASP.NET markup.
In my case there is no possibility to change to CompositeControls.
Trust me, i prefer Composite/WebControls ...