So I have one main Form that works as the navigation bar and two UserControls that display some controls.
In UserControlsA I have some fields that require to be filled. With that data I create an Object that contains some information. I require to pass that object to UserControlsB so I can display some data there.
My idea was to make three instances of the object, one in the UserControlsA to get the information required for the object, one in the main form to get a "copy" of the object from UserControlsA, and one in UserControlsB that can get the information from the main Form.
However, this seems redundant and doesn't even work. Here's some code:
Main Form:
public partial class main : Form
{
public Object object { get; set; }
public UCA uca;
public UCB ucb;
public Form1()
{
InitializeComponent();
uca = new UCA();
ucb = new UCB();
panel2.Controls.Add(uca);
panel2.Controls.Add(ucb);
ucb.Visible = false;
uca.Visible = true;
}
private void button1_Click(object sender, EventArgs e)
{
ucb.Visible = false;
uca.Visible = true;
}
private void button2_Click(object sender, EventArgs e)
{
ucb.Visible = true;
uca.Visible = false;
}
}
UserControlsA:
public partial class UCA : UserControl
{
public Object object { get; set; }
public UCA()
{
InitializeComponent();
}
private void bUsage_Click(object sender, EventArgs e)
{
//Data is provided
object = new Object(data);
//I use var parent to try and access the object from the main form.
var parent = Parent as Form1;
object = parent.object;
}
}
UsercontrolB:
public partial class UCB : UserControl
{
public Object object { get; set; }
public UCB()
{
InitializeComponent();
}
public void updateData()
{
//I try to assign the object from the main form to this form's object.
var parent = Parent as Form1;
object = parent.object;
}
}
Using var Parent doesn't work. What can I do?
A couple of examples using the INotifyPropertyChanged Interface and an implementation that makes use of standard public events.
Related Documentation:
Windows Forms Data Binding
Change Notification in Windows Forms Data Binding
Interfaces Related to Data Binding
Using INotifyPropertyChanged:
The UserControl exposes a public Property (here, named CustomDataObject, simple string Type in the first example, object in the second. It can another Type of course).
The Property is decorated with the Bindable attribute. The BindingDirection here is more a description of the intent, there's no Template attached to it.
Two other standard Attributes are added:
DefaultValue defines the default value of a Property (the value assigned to the Property when the Control is created). It's used by the Code Generator to determine whether the current value should be serialized: it's not serialized if it matches the value set by the Attribute.
It's also used by the PropertyGrid to show, in bold, a non-default value selection or assignment.
DesignerSerializationVisibility specifies the how the Property should be serialized at design-time. Here, is set to DesignerSerializationVisibility.Visible, to signify that the Property should be serialized.
The INotifyPropertyChanged Interface can be seen as a simplified way to add Property bindings to more than one property, using the same event handler, to notify a change in value.
The default implementation of the Interface simply requires that a a public Event of type PropertyChangedEventHandler is added to the class.
When a Property value is changed, the setter just invokes the Event. There are slightly different ways to perform this action; here I'm using a OnPropertyChanged() method that uses the CallerMemberName Attribute to acquire the name of the Property that calls it. It's fairly common in both WinForms and WPF.
UCA UserControl:
The UserControl (see the visual example), has two Buttons that change the bound CustomDataObject Property value. Their Click action is handled by ButtonsAction_Click.
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
public partial class UCA : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string m_DataObject = string.Empty;
public UCA() => InitializeComponent();
[Bindable(true, BindingDirection.TwoWay), DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string CustomDataObject {
get => m_DataObject;
set {
if (m_DataObject != value){
m_DataObject = value;
OnPropertyChanged();
}
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private void ButtonsAction_Click(object sender, EventArgs e)
{
var btn = sender as Button;
CustomDataObject = (btn == SomeButton) ? txtInput1.Text : txtInput2.Text;
}
}
UCB UserControl:
This other UserControl is the receiver. It just exposes a public Property (ReceiverDataObject) that will be bound to the CustomDataObject Property of UCA.
The ReceiverDataObject property is also defined as [Bindable], with the intention of making it one-way only. The property doesn't raise any event. It receive a value, stores it in a private Field and sets an internal UI element.
public partial class UCB : UserControl
{
private string m_RecvDataObject = string.Empty;
public UCB() => InitializeComponent();
[Bindable(true, BindingDirection.OneWay)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string ReceiverDataObject {
get => m_RecvDataObject;
set {
m_RecvDataObject = value;
txtPresenter.Text = m_RecvDataObject;
}
}
}
Using Standard Events notifications:
You can also generate Property change notifications using standard Events.
The difference is that you need an Event for each Property that should notify changes.
If you already have Event delegates used for this, then it's probably a good choice, since there's very few to add: just call the protected method that raises the Event in the Property setter.
Here, I'm, using the common .Net Event handling, using the EventHandlerList defined by the underlying Component class and exposed by its Events property, to add remove event subscriptions.
The Events are usually raised calling a protected method that has the same name of the Event, except the On prefix.
Here, CustomDataObjectChanged Event => OnCustomDataObjectChanged() method.
You can see this pattern in all standard Controls.
▶ The CustomDataObjectChanged name assigned to the Event is not a choice: this event must have the same name of the Property and the Changed suffix.
This is the pattern, it's enough to just follow it.
UCA UserControl:
public partial class UCA : UserControl
{
private static readonly object Event_CustomDataObjectChanged = new object();
private object m_DataObject = null;
public UCButtonActions() => InitializeComponent();
[Bindable(BindableSupport.Yes, BindingDirection.TwoWay), DefaultValue(null)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public object CustomDataObject {
get => m_DataObject;
set {
if (m_DataObject != value){
m_DataObject = value;
OnCustomDataObjectChanged(EventArgs.Empty);
}
}
}
public event EventHandler CustomDataObjectChanged {
add {
Events.AddHandler(Event_CustomDataObjectChanged, value);
}
remove {
Events.RemoveHandler(Event_CustomDataObjectChanged, value);
}
}
protected virtual void OnCustomDataObjectChanged(EventArgs e)
{
if (Events[Event_CustomDataObjectChanged] is EventHandler evth) evth(this, e);
}
}
UCB UserControl:
The second UserControl doesn't change. It's just the receiver.
The Form class (or another class used as Handler):
In the Form Constructor, or any other method called after the Form initialization, use the DataBindings property of UCB to link the Properties of the two UserControls:
public SomeForm()
{
InitializeComponent();
ucb1.DataBindings.Add("ReceiverDataObject", uca1, "CustomDataObject",
false, DataSourceUpdateMode.OnPropertyChanged);
}
You can also use a BindingSource to mediate:
BindingSource ucsSource = null;
public SomeForm()
{
InitializeComponent();
ucsSource = new BindingSource(uca1, null);
ucb1.DataBindings.Add("ReceiverDataObject", ucsSource, "CustomDataObject",
false, DataSourceUpdateMode.OnPropertyChanged);
}
Sample functionality:
Maybe you should redesign your data flow. UserControl should not usually make assumptions of what its parent would be, that's why it's a "customized control". It can be a Form1 but not necessary. So you shouldn't do casting like in your example.
To provide the information from A to B, one way is to create public Get/Set methods or properties for those controls. And the main form works with those public members, pseudo-code can be:
class main{
UCA uca;
UCB ucb;
public void RefreshData(){
object data = uca.GetData();
ucb.UpdateData(data);
}
}
So I just learned how to properly use events I guess. Here's how the code looks now:
Main form:
public partial class main : Form
{
public UCA uca;
public UCB ucb;
public delegate void passObject(object source, someObject u);
public Form1()
{
InitializeComponent();
uca = new UCA();
ucb = new UCB();
panel2.Controls.Add(uca);
panel2.Controls.Add(ucb);
ucb.Visible = false;
uca.Visible = true;
}
private void Form1_Load(object sender, EventArgs e)
{
uca.objectRequired += ucb.ucb_objectRequired;
}
private void button1_Click(object sender, EventArgs e)
{
ucb.Visible = false;
uca.Visible = true;
}
private void button2_Click(object sender, EventArgs e)
{
ucb.Visible = true;
uca.Visible = false;
}
}
Usercontrol A:
public partial class UCA : UserControl
{
public someObject o { get; set; }
public event passObject objectRequired;
public UCA()
{
InitializeComponent();
}
private void bUsage_Click(object sender, EventArgs e)
{
//Data is provided
o = new someObject(data);
usageRequired?.Invoke(this, o);
}
}
Usercontrol B:
public partial class UCB : UserControl
{
public SomeObject o { get; set; }
public UCDetails()
{
InitializeComponent();
}
public void ucn_objectRequired(object sender, sObject u)
{
o = u;
//Use the data from the object.
}
}
Related
I have classes called ButtonDesign, TextBoxDesign, and a few more. Everyone’s realization is exactly the same. I add a number of properties and functions to each control, the question is how can this be achieved without duplicate code, i.e .: is there a way to create only one class with the same attributes - and these attributes will be added to all the controls I want? .
If I create a primary class that inherits from the Control class - then these attributes will only be in the Control class and not in all the controls I want.
This is the code I'm trying to:
class ButtonDesign : Button
{
private Control save_properties = new Control();
public Color OnMouseHoverColor { get; set; }
public ButtonDesign()
{
this.MouseEnter += ButtonDesign_MouseEnter;
this.MouseLeave += ButtonDesign_MouseLeave;
}
private void ButtonDesign_MouseEnter(object sender, EventArgs e)
{
save_properties.BackColor = this.BackColor;
this.BackColor = this.OnMouseHvetColor;
}
private void ButtonDesign_MouseLeave(object sender, EventArgs e)
{
this.BackColor = save_properties.BackColor;
}
}
class TextBoxDesign : TextBox
{
private Control save_properties = new Control();
public Color OnMouseHoverColor { get; set; }
public TextBoxDesign()
{
this.MouseEnter += ButtonDesign_MouseEnter;
this.MouseLeave += ButtonDesign_MouseLeave;
}
private void TextBoxDesign_MouseEnter(object sender, EventArgs e)
{
save_properties.BackColor = this.BackColor;
this.BackColor = this.OnMouseHvetColor;
}
private void TextBoxDesign_MouseLeave(object sender, EventArgs e)
{
this.BackColor = save_properties.BackColor;
}
}
Congratulations, you've encountered the diamond inheritance problem. C# doesn't support this scenario, so what you're attempting to do is unfortunately not possible.
Your only options are:
Create a subclass of Control and make your custom controls inherit from it. This means you have to recreate the behaviour of the standard WinForms controls in your subclasses, which is painful at best.
public class MyControl : Control {}
public class MyButton : MyControl {}
Create an interface that your common controls implement. Have the interface implementations delegate to a shared library that does what needs to be done:
public interface IMyControl
{
void MySharedOperation();
}
public class MyButton : Button, IMyControl
{
public void MySharedOperation()
=> MySharedOperationHandler.MySharedOperation(this);
}
public static class MySharedOperationHandler
{
public static void MySharedOperation(Control control) {}
}
You'll end up with a fair amount of method implementations that do nothing more than delegate, but IMO this is far better than reinventing the control wheel as in the previous option.
If you want this, your class ButtonDesign is not a Button, it has a Button and a Layout. Similarly your class TextBoxDesign has a TextBox and a Layout.
In other words: don't use inheritance, use aggregation!
Every property that are both in Buttons, TextBoxes, and other Controls that have the properties that you want to change for all items in one statement, create a class that contains all these properties, with the proper events.
For every property you need a private member, a public get/set property and an event that will be raised when the property changes. Something like this:
class Layout
{
private Color backColor; // TODO: give proper initial values
private Color foreColor;
... // other properties that you want to change for all Controls
// Events:
public event EventHander BackColorChanged;
public event EventHandler ForeColorChanged;
... // etc
// Event raisers:
protected virtual void OnBackColorChanged()
{
this.BackColorChanged?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnForColorChanged()
{
this.ForeColorChanged?.Invoke(this, EventArgs.Empty);
}
...
// Properties:
public Color BackColor
{
get => this.backColor;
set => if (this.BackColor != value) this.OnBackColorChanged();
}
public Color ForeColor ...
public Size Size ...
}
Your DesignButton will be a UserControl that has a Docked Button and a Layout. The constructor subscribes to the events. When raise the corresponding property on the button is set.
Use the visual studio designer to create the UserControl. Code will be similar to the following:
class MyButton : UserControl
{
private Button button; // visual studio designer will create this
private Layout layout;
public Layout Layout
{
get => this.layout;
set => if (this.Layout != value) this.ChangeLayout;
}
// you can't set the design properties. Only get.
protected Button Button => this.button;
Color BackColor
{
public get => this.Button.BackColor;
private set => this.Button.BackColor = value;
}
// etc for ForeColor, Text, ...
protected virtual void ChangeLayout(Layout newLayout)
{
// TODO: if there is an old Layout: desubscribe from all events from old Layout
this.layout = newLayout;
// subscribe to all events:
this.layout.BackColorChanged += this.BackColor_Changed;
this.layout.forColorChanged += this.ForColor_Changed;
...
this.BackColor = this.BackColor;
// TODO: if desired, for completeness add an event: LayoutChanged
}
Now whenever Layout raises event BackColorChanged you handle this event and assign the value to the Button. You'll get the gist.
Do something similar for TextBoxes, ComboBoxes, etc
Usage:
Layout commonLayout = new Layout
{
BackColor = Color.Yellow,
ForeColor = color.Black,
...
};
MyButton button1 = new MyButton
{
Layout = commonLayout,
};
MyTextBox textBox1 = new MyTextBox
{
Layout = commonLayout,
}
MyComboBox comboBox1 = ...
// Change the backgroundColor for all items:
commonLayout.BackColor = Color.Red;
If you have a lot of properties, consider to use generic classes
class LayoutProperty<T>
{
private T propertyValue;
public event eventHandler PropertyValueChanged
protected void OnPropertyValueChanged()
{
this.PropertyValueChanged?.Invoke(this, EventArgs.Empty);
}
public T PropertyValue
{
get => this.propertyValue;
set => if (this.PropertyValue != value) this.OnPropertyValueChanged();
}
}
class Layout
{
private PropertyValue<Color> backColor;
private PropertyValue<Color> foreColor;
// etc, see above for subscribtion and raising events.
And for all Controls:
public MyControl
{
private Control control;
private Layout layout;
// etc, see above for the event handling.
}
public MyButton : MyControl {Control = new Button()}
public MyTextBox : MyControl {Control = new Textbox()}
On my form, I have one Panel container, named "panelShowList".
On my project, i added a new class, which look like this:
class myNewClass
{
private int newPanelPos = 30;
private const int spaceBetweenElements = 30;
private const int panelWidth = 90;
private const int panelHeight = 40;
private int elementPos = 0;
private ArrayList myPanels = new ArrayList() { };
// some irelevant methods
public void addElementPanels(Panel dataPanel, Panel nextPanel)
{
myPanels.Add(dataPanel);
myPanels.Add(nextPanel);
}
public void displayPanels()
{
foreach (Panel tmp in myPanels)
{
// here i'm stuck
// i need to do something like this :
// myMainForm.panelShowList.Controls.Add(tmp);
// of course this is wrong! but i need a method to acces that control
}
}
}
Basically, I need a way to add all Panels from my ArrayList on "panelShowList" control from my form.
I tried something like this:
public void displayPanels()
{
frmMain f = new frmMain();
foreach (Panel tmp in myPanels)
{
f.display(tmp);
// where display(Panel tmp) is a function in my Form, who access
// "panelShowList" control and add a new Panel
}
}
But it only works if i do this:
f.ShowDialog();
and another form is open.
Any suggestions will be appreciated.
Maybe a bit late, but by all means, here is another approach, that's still more clean than David's approach:
You should add an EventHandler in your MyNewClass. Then you can subscribe to that event from within your form.
public partial class Form1 : Form
{
private readonly MyNewClass _myNewClass;
public Form1()
{
InitializeComponent();
_myNewClass = new MyNewClass();
_myNewClass.DisplayPanelsInvoked += DisplayPanelsInvoked;
}
private void DisplayPanelsInvoked(object sender, DisplayPanelsEventArgs e)
{
var panels = e.Panels; // Add the panels somewhere on the UI ;)
}
}
internal class MyNewClass
{
private IList<Panel> _panels = new List<Panel>();
public void AddPanel(Panel panel)
{
_panels.Add(panel);
}
public void DisplayPanels()
{
OnDisplayPanels(new DisplayPanelsEventArgs(_panels));
}
protected virtual void OnDisplayPanels(DisplayPanelsEventArgs e)
{
EventHandler<DisplayPanelsEventArgs> handler = DisplayPanelsInvoked;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<DisplayPanelsEventArgs> DisplayPanelsInvoked;
}
internal class DisplayPanelsEventArgs : EventArgs
{
public DisplayPanelsEventArgs(IList<Panel> panels)
{
Panels = panels;
}
public IList<Panel> Panels { get; private set; }
}
In my opinion it's a better solution, because you don't need to provide a reference of the form to the MyNewClass instance. So this approach reduces coupling, because only the form has a dependency to the MyNewClass.
If you always want to "update" the form whenever a panel is added, you could remove the DisplayPanels-method and shorten the code to this:
public partial class Form1 : Form
{
private readonly MyNewClass _myNewClass;
public Form1()
{
InitializeComponent();
_myNewClass = new MyNewClass();
_myNewClass.PanelAdded += PanelAdded;
}
private void PanelAdded(object sender, DisplayPanelsEventArgs e)
{
var panels = e.AllPanels; // Add the panels somewhere on the UI ;)
}
}
internal class MyNewClass
{
private IList<Panel> _panels = new List<Panel>();
public void AddPanel(Panel panel)
{
_panels.Add(panel);
OnPanelAdded(new DisplayPanelsEventArgs(_panels, panel)); // raise event, everytime a panel is added
}
protected virtual void OnPanelAdded(DisplayPanelsEventArgs e)
{
EventHandler<DisplayPanelsEventArgs> handler = PanelAdded;
if (handler != null)
{
handler(this, e);
}
}
public event EventHandler<DisplayPanelsEventArgs> PanelAdded;
}
internal class DisplayPanelsEventArgs : EventArgs
{
public DisplayPanelsEventArgs(IList<Panel> allPanels, Panel panelAddedLast)
{
AllPanels = allPanels;
PanelAddedLast = panelAddedLast;
}
public IList<Panel> AllPanels { get; private set; }
public Panel PanelAddedLast { get; private set; }
}
and another form is open
That's because you're creating an entirely new form:
frmMain f = new frmMain();
If you want to modify the state of an existing form, that code will need a reference to that form. There are a number of ways to do this. One could be to simply pass a reference to that method:
public void displayPanels(frmMain myMainForm)
{
foreach (Panel tmp in myPanels)
{
// myMainForm.panelShowList.Controls.Add(tmp);
// etc.
}
}
Then when your main form invokes that method, it supplies a reference to itself:
instanceOfNewClass.displayPanels(this);
Though, to be honest, it's not really clear what sort of structure you're going for here. If code is modifying a form then I imagine that code should be on that form. It can certainly be organized into a class, but perhaps that can be an inner class of that form since nothing else needs to know about it.
I'm also concerned that your implementation of myNewClass requires methods to be invoked in a specific order. Any given operation on an object should fully encapsulate the logic to complete that operation. Some of that initialization logic may belong in the constructor if the object isn't in a valid state until that logic is completed.
This is all a bit conjecture though, since the object structure isn't clear here.
In my WPF MVVM app, using Caliburn.Micro, I have a ViewModel, CreateServiceViewModel that, on a button click, opens a GridView in a seperate window for the User to chose a Row from.
I created another ViewModel for this, MemberSearchViewModel which has two properties:
private Member selectedMember;
public Member SelectedMember
{
get { return selectedMember; }
set { selectedMember = value; }
}
private IList<Member> members;
public IList<Member> Members
{
get { return members; }
set { members = value; }
}
How do I get that SelectedMember value back to the calling ViewModel? That ViewModel has a property of Service.SelectedMember.
EventAggregator is what you could use... One of many solutions I am sure.
public class MessageNotifier{
public object Content{get;set;}
public string Message {get;set;}
}
//MEF bits here
public class HelloWorldViewModel: Screen, IHandle<MessageNotifier>{
private readonly IEventAggregator _eventAggregator
//MEF constructor bits
public YourViewModel(IEventAggregator eventAggregator){
_eventAggregator = eventAggregator;
}
public override OnActivate(){
_eventAggregator.Subscribe(this);
}
public override OnDeactivate(){
_eventAggregator.UnSubscribe(this);
}
//I Handle all messages with this signature and if the message applies to me do something
//
public void Handle(MesssageNotifier _notifier){
if(_notifier.Message == "NewSelectedItem"){
//do something with the content of the selectedItem
var x = _notifier.Content
}
}
}
//MEF attrs
public class HelloWorld2ViewModel: Screen{
private readonly IEventAggregator _eventAggregator
//MEF attrs
public HelloWorld2ViewModel(IEventAggregator eventAggregator){
_eventAggregator = eventAggregator;
}
public someobject SelectedItem{
get{ return _someobject ;}
set{ _someobject = value;
NotifyOfPropertyChange(()=>SelectedItem);
_eventAggregator.Publish(new MessageNotifier(){ Content = SelectedItem, Message="NewSelectedItem"});
}
}
One option is to utilize NotifyPropertyChanged. Since you are working with ViewModels, they most likely implement INotifyPropertyChanged, which you can make use of just as the framework does.
When your CreateServiceViewModel creates the MemberSearchViewModel, it would just subscribe to the PropertyChanged event:
//This goes wherever you create your child view model
var memberSearchViewModel = new MemberSearchViewModel(); //Or using a service locator, if applicable
memberSearchViewModel.PropertyChanged += OnMemberSearchPropertyChanged;
private void OnMemberSearchPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "SelectedMember")
{
//Code to respond to a change in the Member
}
}
And then in your MemberSearchViewModel, you simply raise the NotifyPropertyChanged event when the user has selected a member from the grid.
EDIT:
As #DNH correctly notes in the comments, using event handlers like this can lead to memory leaks if not properly cleaned up. So when you are finished with the MemberSearchViewModel, make sure to unsubscribe to the PropertyChanged event. So for example, if you only need it until the user selects a member, you could put it inside the Property Changed Handler itself (I've switched it to use a class-level variable to hold the ViewModel):
private void OnMemberSearchPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "SelectedMember")
{
//Code to respond to a change in the Member
//Unsubscribe so the view model can be garbage collected
_memberSearchViewModel.PropertyChanged -= OnMemberSearchPropertyChanged;
_memberSearchViewModel = null;
}
}
One option would be to store MemberSearchViewModel as a field of CreateServiceViewModel and define CreateServiceViewModel.SelectedMember property as follows:
public Member SelectedMember
{
get
{
return _memberSearchViewModel.SelectedMember;
}
set
{
_memberSearchViewModel.SelectedMember = value;
}
}
How about?
public interface INotifyMe<T>
{
T ResultToNotify { get; set; }
}
public class CreateServiceViewModel : ViewModelBase, INotifyMe<Member>
{
// implement the interface as you like...
}
public class MemberSearchViewModel : ViewModelBase
{
public MemberSearchViewModel(INotifyMe<Member> toBeNotified)
{
// initialize field and so on...
}
}
Now you could let listen CreateServiceViewModel to its own property and you won't have to think about the removal of the event listener.
Well of course to do the more classical way you could alternatively use an interface like this.
public interface INotifyMe<T>
{
void Notify(T result);
}
As a follow-up to my comment, here's an example using Prism - I've never used Caliburn.
Create an event - the event's payload will be your SelectedMember:
public class YourEvent:CompositePresentationEvent<YourEventPayload>{}
Publish the event:
EventAggregator.GetEvent<YourEvent>().Publish(YourEventPayload);
Subscribe to the event:
EventAggregator.GetEvent<YourEvent>().Subscribe((i) => ...);
Basically I've got a command binding for the command itself assigned to Window.CommandBindings:
<CommandBinding Command="local:TimerViewModel.AddTimer"
CanExecute="local:TimerViewModel.AddTimer_CanExecute"
Executed="local:TimerViewModel.AddTimer_Executed" />
local is a namespace generated by default pointing to the namespace of the application. What I'm trying to achieve here is to have the command handling inside the TimerViewModel but I keep getting the following error:
CanExecute="local:TimerViewModel.AddTimer_CanExecute" is not valid. 'local:TimerViewModel.AddTimer_CanExecute' is not a valid event handler method name. Only instance methods on the generated or code-behind class are valid.
The TimerViewModel is pretty simple though but I believe I am missing something:
public class TimerViewModel : ViewModelBase
{
public TimerViewModel()
{
_timers = new ObservableCollection<TimerModel>();
_addTimer = new RoutedUICommand("Add Timer", "AddTimer", GetType());
}
private ObservableCollection<TimerModel> _timers;
public ObservableCollection<TimerModel> Timers
{
get { return _timers; }
}
private static RoutedUICommand _addTimer;
public static RoutedUICommand AddTimer
{
get { return _addTimer; }
}
public void AddTimer_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
public void AddTimer_Executed(object sender, ExecutedRoutedEventArgs e)
{
_timers.Add(new TimerModel(TimeSpan.FromSeconds((new Random()).Next())));
}
}
Can anyone point out the mistakes I'm making?
Also take a look at Josh Smith's RelayCommand. Using it would enable you to write the above like this:
public class TimerViewModel : ViewModelBase {
public TimerViewModel() {
Timers = new ObservableCollection<TimerModel>();
AddTimerCommand = new RelayCommand(() => AddTimer());
}
public ObservableCollection<TimerModel> Timers {
get;
private set;
}
public ICommand AddTimerCommand {
get;
private set;
}
private void AddTimer() {
Timers.Add(new TimerModel(TimeSpan.FromSeconds((new Random()).Next())));
}
}
Take a look at http://www.wpftutorial.net/DelegateCommand.html for an example of how to implement the delegate command for WPF. It allows you to hook up Execute and CanExecute as event handlers. If you're using RoutedUICommand directly you need to derive a custom command from it and override Execute and CanExecute with your functions.
I want to bind a custom property of a windows form to a second property, so when I update the former the latter gets the same value.
This is the simplest example of what I'm trying to do:
public partial class Form2 : Form
{
public string MyTargetProperty { get; set; }
public string OtherProperty { get; set; }
public Form2()
{
InitializeComponent();
this.DataBindings.Add("MyTargetProperty", this, "OtherProperty");
}
private void button1_Click(object sender, EventArgs e)
{
MyTargetProperty = "test";
Console.WriteLine("OtherProperty " + OtherProperty);
}
}
When I click button1 I should be able to see that 'OtherProperty' has the same value as 'MyTargetProperty'. Am I doing something wrong? Do I miss something?
Your form needs to implement INotifyPropertyChanged for the MyTargetProperty.
Example:
class FooForm : Form, INotifyPropertyChanged
{
private int myTargetProperty;
public int MyTargetProperty
{
get { return this.myTargetProperty; }
set
{
this.myTargetProperty = value;
this.OnPropertyChanged(
new PropertyChangedEventArgs("MyTargetProperty"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
var evt = this.PropertyChanged;
if (evt != null)
evt(this, e);
}
}
Then you need to add the binding like this:
this.DataBindings.Add(
"OtherProperty",
this,
"MyTargetProperty",
false,
DataSourceUpdateMode.Never);
This will perform a one way binding. The MyTargetProperty will never be updated when the OtherProperty changes. If you need a two way binding you change the DataSourceUpdateMode and also implement a PropertyChanged for OtherProperty.