Is there an easy way to link two objects property together? - c#

Using c# and winforms with .NET 4.5 I would like to link the Checked property of a menu item with the Visible property of a form.
Changing any of these two attribute would change the other one to keep them synchronized.
Is there an easy and elegant solution to do that ?

Something like this example with a checkbox and a button:
Wire up to the CheckedChanged event
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
var checkBox = sender as CheckBox;
button1.Visible = !checkBox.Checked;
}
Edit:
Ok, i misunderstood.
Although the solution of 'farid' is a clean solution with separation of concerns using viewmodel and model it also adds more complexity to your application.
If you don't want to use this mvvm pattern and put the logic in the code behind you can implement the INotifyPropertyChanged interface to the form that has the visible property (or add a custom event), add a new Visible property that sets the base.visible property (inherited by the Form from the Control class) and raise the PropertyChanged event. in the form that contains the menu item you can wire up to the event and perform the necessary logic to set the checked state or do some other action.
Here is an example:
Form1 code behind:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += new System.EventHandler(this.Form1_Load);
}
private Form2 _frm2;
private void Form1_Load(object sender, EventArgs e)
{
_frm2 = new Form2();
_frm2.MdiParent = this;
_frm2.PropertyChanged += _frm2_PropertyChanged;
_frm2.Show();
}
void _frm2_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Visible")
{
showToolStripMenuItem.Checked = _frm2.Visible;
}
}
private void showToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
{
var menuItem = sender as ToolStripMenuItem;
if (_frm2 != null)
_frm2.Visible = menuItem.Checked;
}
}
Form2 code behind:
public partial class Form2 : Form, INotifyPropertyChanged
{
public Form2()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
public new bool Visible
{
get
{
return base.Visible;
}
set
{
base.Visible = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Visible"));
}
}
private void hideButton_Click(object sender, EventArgs e)
{
Visible = false;
}
}

You can use Windows Forms Data Bindings.
Every windows forms control has a DataBindings property that can be used to bind a property in given data source to one of controls properties.
You can structure your code like example below:
This example shows binding from a ViewModel object to a control property. In your specific case you can bind a ViewModel property to two control property.
public partial class StackOverflowForm : Form
{
public ViewModel Model { get; set; }
public Dictionary<string, Control> BindableControls { get; set; }
public StackOverflowForm()
{
Model = new ViewModel();
Model.PropertyChanged += Model_PropertyChanged;
BindableControls = new Dictionary<string, Control>();
Model.Visible = false;
InitializeComponent();
RegisterBinding(boundButton, "Visible", Model, "Visible");
}
void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
foreach (var item in BindableControls)
{
NotifyChange(item.Value, e.PropertyName);
}
}
private void NotifyChange(Control control, string propertyName)
{
button1.DataBindings[propertyName].ReadValue();
}
private void RegisterBinding(Control control, string controlPropertyName, ViewModel _model, string modelPropertyName)
{
control.DataBindings.Add(controlPropertyName, _model, modelPropertyName, true, DataSourceUpdateMode.OnPropertyChanged);
BindableControls[control.Name] = control;
}
private void SetPropertyButton_Click(object sender, EventArgs e)
{
Model.Visible = true;
}
}
public class ViewModel : INotifyPropertyChanged
{
private bool _IsVisible;
public bool Visible
{
get
{
return _IsVisible;
}
set
{
_IsVisible = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Visible"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
I have defined a ViewModel object in the form that is used as binding data source for controls. (that have a Visible property)
private void RegisterBinding(Control control, string controlPropertyName, ViewModel _model, string modelPropertyName)
{
control.DataBindings.Add(controlPropertyName, _model, modelPropertyName, true, DataSourceUpdateMode.OnPropertyChanged);
BindableControls[control.Name] = control;
}
Use RegisterBinding method to register simple binding (parameters are simple enough).
'ViewModel' class implements INotifyPropertyChanged interface in System.ComponentModel. This interface adds a PropertyChanged event to call when any property in ViewModel changes.
In form's constructor I added event listener to PropertyChanged event of ViewModel in listener I forced the binding to read new value for each of controls that a binding registered for it. This portion of code refresh's the bound control and changes the visible state of button.
NOTE: In order to answer simplicity I assumed that properties in ViewModel that bound to a control's property has SAME name with destination property in form control. (Mode.Visible and boundButton.Visible). If you want to implement property name mapping for source and destination properties you can use a Dictionary or something to achieve this functionality.

Related

C# Composite Control will lose name at design and run time

I developed a composite control using C# Windows Forms Control Library and code as follow:
public partial class MyControl : UserControl
{
public event EventHandler NameChanged;
protected virtual void OnNameChanged()
{
EventHandler handler = NameChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
private void WhenNameChanged(object sender , EventArgs e)
{
this.myGroupBox.Text = this.Name;
}
protected override void OnCreateControl()
{
base.OnCreateControl();
IComponentChangeService changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService));
if (changeService == null) return; // not provided at runtime, only design mode
changeService.ComponentChanged -= OnComponentChanged; // to avoid multiple subscriptions
changeService.ComponentChanged += OnComponentChanged;
}
private void OnComponentChanged(object sender, ComponentChangedEventArgs e)
{
if(e.Component == this && e.Member.Name == "Name")
{
OnNameChanged();
}
}
public MyControl()
{
InitializeComponent();
this.NameChanged += new EventHandler(this.WhenNameChanged);
}
}
The MyControl only has one GroupBox control named myGroupBox
private System.Windows.Forms.GroupBox myGroupBox;
I have a test program which is a C# Windows Forms Application, and I would like to see that when I change the name of myGroupBox in properties window, the text of myGroupBox will be the name I typed. so I typed a name Value to myGroupBox in the properties window, the text will change in designer, here is the screenshot:
But when I rebuild the test program or at run time the text of myGroupBox will disappear, here is the screenshot:
what should I do with my code? thanks
So the problem is that the Name of a Control (or UserControl) is empty after construction. The name given by you and stored in the resources will be set in OnLoad.
So a solution for you could be to override OnLoad with something like this:
public class MyControl : UserControl
{
// ... the code so far
// override OnLoad
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
myGroupBox.Text = Name; // or WhenNameChanged(this, EventArgs.Empty);
}
}
So the name taken from the resources (e.g. after rebuild/reopen designer) will be set here again and so also set as myGroupBox.Text.
Hope that helps.

How to access a textbox in a form from other form [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
There are two forms, each one has a textbox. my question is
how to access the textbox on form 1 by form 2
and
how to access the textbox on form 2 by form 1?
I used
Form1 ths;
public Form2(Form1 my_form_1)
{
InitializeComponent();
ths = my_form_1;
}
but I cannot access form2 from form1!!!
TQ
Instead of accessing the TextBox directly, you could share a ViewModel that offers the Properties you need to both Forms (or any other controls afterwards)
For this, you could create your own ViewModel using the INotifyPropertyChanged implementation, that notifies any listeners that a property has changed. An example of such a class would be the following
using System.ComponentModel;
namespace SimpleViewModel
{
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var local = PropertyChanged;
if (local != null)
{
local.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
protected string result;
public string Result
{
get
{
return result;
}
set
{
if (string.Equals(result, value))
{
return;
}
result = value;
RaisePropertyChanged("Result");
}
}
}
}
When it's Result property would change, the class would notify any listeners that are registered to the PropertyChanged event of the class. The classes that listen to those changes can then choose how to handle or ignore this changed (eg, based on PropertyName) like such in Form2 (i called it ResultForm)
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace SimpleViewModel
{
public partial class ResultForm : Form
{
protected ViewModel viewModel;
public ViewModel ViewModel
{
get
{
return viewModel;
}
set
{
if (object.Equals(ViewModel, value))
{
return;
}
if (ViewModel != null)
{
viewModel.PropertyChanged -= OnViewModelChanged;
}
viewModel = value;
if (ViewModel != null)
{
viewModel.PropertyChanged += OnViewModelChanged;
}
}
}
protected virtual void OnViewModelChanged(object sender, PropertyChangedEventArgs e)
{
var vm = sender as ViewModel;
if (vm == null)
{
return;
}
if (e.PropertyName == "Result")
{
lblResult.Text = vm.Result;
}
}
public ResultForm()
{
InitializeComponent();
}
public ResultForm(ViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
}
protected override void OnClosed(EventArgs e)
{
ViewModel = null;
base.OnClosed(e);
}
}
}
Here we register to the PropertyChanged event when the ViewModel property is changed (or set by use of the constructor). When we get the property changed event, we check if it is indeed the "Result" property that changed, and if so, update our lblResult accordingly (could be your TextBox)
In form1 we could create the ViewModel, and this ViewModel is then given to the ResultForm, for sake of a testcase, i added it to the Form_Load event, although the important part here is simply that the ViewModel is the same for both forms (it could be a singleton class, ... depending on your need)
using System;
using System.Windows.Forms;
namespace SimpleViewModel
{
public partial class Form1 : Form
{
protected ViewModel MyResultViewModel = new ViewModel();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ResultForm form = new ResultForm(MyResultViewModel);
form.Show();
MyResultViewModel.Result = "42";
}
}
}
By setting the MyResultViewModel.Result property, the changes are send to the ResultForm and we see 42 in the Label
When you're creating the Instance of Form2 in Form1 you could just
public void yourMethod()
{
Form2 form = new Form2(this);
form.Show();
form.textbox.Text = "xy";
}

I need help creating a class to store data for my entire project including user controls

I am still very new to WPF and I've spent a week trying to understand MVVM with not very much luck, but I am not specifically trying to follow those rules, I just need to store information into a class so all of my user controls can access it and also change it.
This is what I have so far:
namespace WpfApplication2.Funtions
{
public class Binder : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private string _Test_String;
public string Test_String
{
get { return _Test_String; }
set { _Test_String = value; OnPropertyChanged("Test_String"); }
}
}
}
User control:
public partial class UserControl1 : UserControl
{
//Funtions.Binder _B = new Funtions.Binder();
public UserControl1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MainWindow MW = new MainWindow();
textBox1.Text = MW._B.Test_String;
}
}
Mainwindow:
public partial class MainWindow : Window
{
public Funtions.Binder _B = new Funtions.Binder();
public MainWindow()
{
InitializeComponent();
_B.Test_String = "HELLO";
}
private void button1_Click(object sender, RoutedEventArgs e)
{
_B.Test_String = "Hello from main";
}
}
I see what is happening here, the user control is creating a new instance of MainWindow so it is NOT the same value, but how do I actually access the same Binder instance that MainWindow created from usercontrol1 so that it can read and change it? or if I can't achieve what I want this way how should I go about doing it? I know there has got to be a very simple solution to this. I don't want to use the project Settings.
Please don't mind the super noob question but I've been stuck for days now, I just need a simple explanation and solution and I'll be on my way.
Thank you.
You need to grab the instance of your main window, not create a new one as you figured out.
private void button1_Click(object sender, RoutedEventArgs e)
{
MainWindow MW = Application.Current.MainWindow;
textBox1.Text = MW._B.Test_String;
}
you should use same instance of main window on usercontrol's button click . like as follows
var mainwindow =Application.Current.MainWindow;
and using same instance get value
textBox1.Text = mainwindow .Test_String;

Get selected List Box item in user control from Window

I have a User control with a list box.
This User control located on my window.
how can I detect and get selected item from list box in user control?
I previously tried this but when i select an item from list box e.OriginalSource return TextBlock type.
private void searchdialog_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
//This return TextBlock type
var conrol= e.OriginalSource;
//I Want something like this
if (e.OriginalSource is ListBoxItem)
{
ListBoxItem Selected = e.OriginalSource as ListBoxItem;
//Do somting
}
}
Or there is any better way that I detect list box SelectionChanged in From My form?
I think the best soution would be to declare an event on your user control, that is fired whenever the SelectedValueChanged event is fired on the listbox.
public class MyUserControl : UserControl
{
public event EventHandler MyListBoxSelectedValueChanged;
public object MyListBoxSelectedValue
{
get { return MyListBox.SelectedValue; }
}
public MyUserControl()
{
MyListBox.SelectedValueChanged += MyListBox_SelectedValueChanged;
}
private void MyListBox_SelectedValueChanged(object sender, EventArgs eventArgs)
{
EventHandler handler = MyListBoxSelectedValueChanged;
if(handler != null)
handler(sender, eventArgs);
}
}
In your window, you listen to the event and use the exposed property in the user control.
public class MyForm : Form
{
public MyForm()
{
MyUserControl.MyListBoxSelectedValueChanged += MyUserControl_MyListBoxSelectedValueChanged;
}
private void MyUserControl_MyListBoxSelectedValueChanged(object sender, EventArgs eventArgs)
{
object selected = MyUserControl.MyListBoxSelectedValue;
}
}
there are a few ways to handle this:
Implement the SelectionChanged event in your usercontrol, and raise a custom event that you handle in your window:
//in your usercontrol
private void OnListBoxSelectionChanged(object s, EventArgs e){
if (e.AddedItems != null && e.AddedItems.Any() && NewItemSelectedEvent != null){
NewItemsSelectedEvent(this, new CustomEventArgs(e.AddedItems[0]))
}
}
//in your window
myUserControl.NewItemsSelected += (s,e) => HandleOnNewItemSelected();
If you use binding or any form of MVVM, you can use a DependencyProperty to bind the selected item to an object in your viewmodel
//in your usercontrol:
public static readonly DependencyProperty CurrentItemProperty =
DependencyProperty.Register("CurrentItem", typeof(MyListBoxItemObject),
typeof(MyUserControl), new PropertyMetadata(default(MyListBoxItemObject)));
public LiveTextBox CurrentItem
{
get { return (MyListBoxItemObject)GetValue(CurrentItemProperty ); }
set { SetValue(CurrentItemProperty , value); }
}
//in your window xaml
<MyUserControl CurrentItem={Binding MyCurrentItem} ... />

Propagating events from one Form to another Form in C#

How can I click a Button in one form and update text in a TextBox in another form?
If you're attempting to use WinForms, you can implement a custom event in your "child" form. You could have that event fire when the button in your "child" form was clicked.
Your "parent" form would then listen for the event and handle it's own TextBox update.
public class ChildForm : Form
{
public delegate SomeEventHandler(object sender, EventArgs e);
public event SomeEventHandler SomeEvent;
// Your code here
}
public class ParentForm : Form
{
ChildForm child = new ChildForm();
child.SomeEvent += new EventHandler(this.HandleSomeEvent);
public void HandleSomeEvent(object sender, EventArgs e)
{
this.someTextBox.Text = "Whatever Text You Want...";
}
}
Roughly; the one form must have a reference to some underlying object holding the text; this object should fire an event on the update of the text; the TextBox in another form should have a delegate subscribing to that event, which will discover that the underlying text has changed; once the TextBox delegate has been informed, the TextBox should query the underlying object for the new value of the text, and update the TextBox with the new text.
Assuming WinForms;
If the textbox is bound to a property of an object, you would implement the INotifyPropertyChanged interface on the object, and notify about the value of the string being changed.
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string title;
public string Title {
get { return title; }
set {
if(value != title)
{
this.title = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Title"));
}
}
}
With the above, if you bind to the Title property - the update will go through 'automatically' to all forms/textboxes that bind to the object. I would recommend this above sending specific events, as this is the common way of notifying binding of updates to object properties.

Categories

Resources