How to maintain object in multiple instances - C# - c#

I am maintaining one object(Parent) in my MainWindow class. That Parent Object is being passed to another object(objMyClass). Now If I update Parent Object in mainwindow, it is not reflecting it in objMyClass object. Below is the code.
using System.Windows;
namespace WpfApp2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public Parent objParent;
public MyClass objMyClass;
public MainWindow()
{
InitializeComponent();
objParent = new Parent();
objMyClass = new MyClass(objParent);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if ((bool)checkPWB.IsChecked)
objParent = new Child1();
else
objParent = new Child2();
objParent.Display();
objMyClass.par.Display();
}
}
public class MyClass
{
public Parent par;
public MyClass(Parent p)
{
par = p;
}
}
public class Parent
{
public virtual void Display()
{
MessageBox.Show("I am Parent");
}
}
public class Child1 : Parent
{
public override void Display()
{
MessageBox.Show("I am Child1");
}
}
public class Child2 : Parent
{
public override void Display()
{
MessageBox.Show("I am Child2");
}
}
}
When I click the button, I am creating a new object (Child1) and assigning to my parent object which doesn't reflect it in ObjectMyClass.
Any help on this is appreciated.

To refer to the same field you can use Func<Parent> that would return current value of the filed:
public class MyClass
{
private Func<Parent> getParent = null;
public Parent par => getParent();
public MyClass(Func<Parent> getParent)
{
this.getParent = getParent;
}
}
and construct your class as
objMyClass = new MyClass(() => objParent);
This way instead of having its own reference to Parent object that contains copy of the original value of the parameter (as in code in the question) this MyClass will always return current value of objParent field and indeed reflect changes to that field.
Alternatively you can just change par property directly instead of changing objParent.

Related

Winforms Databinding Notification

My model is mainly made from the 2 classes below (I actually got another class which inherits from the abstract class but it doesnt matter I think):
public abstract class FeedForEvents: BaseObservableObject
{
public abstract void ReadFeed();
public List<Event> Events { get; set; }
public void AddEvent(Event aEvent)
{
Events.Add(aEvent);
OnPropertyChanged("Events");
}
}
public class Event : BaseObservableObject
{
public string MyProp
{
get
{
return _myProp;
}
set
{
_myprop= value;
OnPropertyChanged();
}
}
}
My form contains:
private BindingList<FeedForEvents> ListFeedsForEvents = new BindingList<FeedForEvents>();
private BindingList<Event> ListEvents
=> new BindingList<Event>(ListFeedsForEvents.SelectMany(m =>m.Events).ToList());
private BindingSource pagesBindingSource = new BindingSource();
public void RefreshGrid()
{
pagesBindingSource.DataSource = ListEvents;
this.grdEvents.DataSource = pagesBindingSource;
this.grdEvents.AutoGenerateColumns = true;
}
But even if my 2 objects correctly raised the PropertyChanged notficiation, the interface never show the objects updated (unless I manually refresh them by pressing a button to manually call RefreshGrid() ). Why?

how to access SimpleChildWindow from other UserControl

I have two user controls in wpf and i have defined simple child window in both of them now i want to access them in the way like Uc1 have child1 and Uc2 have child2 i want to access child2 from Uc1 and vice versa from code behind.
The way I would do that is, via the mainwindow. You don't want to create dependencies between both the controls. This is because of reusing the usercontrols.
This is an example passing the childs thru an event. I wouldn't pass the mainwindow as a reference into the UserControl's constructor.
If you want to do that, you should create an interface and implement it on the MainWindow and pass it as an interface.
like:
UC1-(event)>MainWindow-(methodcall)>UC2-(methodcall)>UC2.child
Pseudo code:
// event args.
public class RequestChildEventArgs : EventArgs
{
public Child2 Child { get;set; }
}
public class UC1
{
// do something when you need the child2
public void DoSomething()
{
var child2 = GetChild2();
if(child2 == null)
// cry.
}
// this method requests a reference of child2
private Child2 GetChild2()
{
// check if the event is assigned.
if(RequestChild == null)
return null;
RequestChildEventArgs args = new RequestChildEventArgs();
RequestChild2(this, args);
return args.Child;
}
public event EventHandler<RequestChildEventArgs> RequestChild2;
}
// user control 2
public class UC2
{
public Child2 Child2 { get; } = new Child2();
}
// the mainwindow that tunnels the Child2
public class MainWindow
{
private UC1 _uc1;
private UC2 _uc2;
public MainWindow()
{
_uc1 = new UC1();
_uc2 = new UC2();
_uc1.RequestChild2 += (s, e) => e.Child = _uc2.Child2;
}
}
the visa versa version:
Pseudo code:
// event args.
public class RequestChildEventArgs<T> : EventArgs
{
public T Child { get; set; }
}
public class UC1
{
public Child1 Child1 { get; } = new Child1();
// do something when you need the child2
public void DoSomething()
{
var child2 = GetChild2();
if (child2 == null)
// cry.
}
// this method requests a reference of child2
private Child2 GetChild2()
{
// check if the event is assigned.
if(RequestChild2 == null)
return null;
RequestChildEventArgs<Child2> args = new RequestChildEventArgs<Child2>();
RequestChild2(this, args);
return args.Child;
}
public event EventHandler<RequestChildEventArgs<Child2>> RequestChild2;
}
// user control 2
public class UC2
{
public Child2 Child2 { get; } = new Child2();
// do something when you need the child1
public void DoSomething()
{
var child1 = GetChild1();
if(child1 == null)
// cry.
}
// this method requests a reference of child1
private Child1 GetChild1()
{
// check if the event is assigned.
if(RequestChild1 == null)
return null;
RequestChildEventArgs<Child1> args = new RequestChildEventArgs<Child1>();
RequestChild1(this, args);
return args.Child;
}
public event EventHandler<RequestChildEventArgs<Child1>> RequestChild1;
}
// the mainwindow that tunnels the Childs
public class MainWindow
{
private UC1 _uc1;
private UC2 _uc2;
public MainWindow()
{
_uc1 = new UC1();
_uc2 = new UC2();
_uc1.RequestChild2 += (s, e) => e.Child = _uc2.Child2;
_uc2.RequestChild1 += (s, e) => e.Child = _uc1.Child1;
}
}
This way is you usercontrol not dependend on the mainwindow or singleton objects.

AccessibleObject implementation for custom controls

I have a very simple controls library for Windows Forms and I am getting problems to implement accessibility.
I have a very simple Form with a member that contains a list of controls of my library, and I have overriden the CreateAccessibilityInstance:
public partial class Form1 : Form
{
protected override AccessibleObject CreateAccessibilityInstance()
{
return new AccessibleForm(this);
}
public MyContainer MyContainer;
public Form1()
{
InitializeComponent();
MyContainer = new MyContainer();
MyContainer.Controls.Add(new MyButton());
}
}
The AccessibleForm class looks like:
public class AccessibleForm: Control.ControlAccessibleObject
{
private Form1 form1;
public AccessibleForm(Form1 owner):base(owner)
{
this.form1 = owner;
}
public override AccessibleObject GetChild(int index)
{
return this.form1.MyContainer.Controls[index].AccessibilityObject;
}
public override int GetChildCount()
{
return this.form1.MyContainer.Controls.Count() ;
}
}
MyContanier and MyButton classes inherits from BaseControl, they are very easy:
public class BaseControl : Control
{
protected override AccessibleObject CreateAccessibilityInstance()
{
return new AccessibleObject();
}
}
public class MyContainer:BaseControl
{
public List<BaseControl> Controls { get; set; }
public MyContainer()
{
this.Controls = new List<BaseControl>();
}
}
public class MyButton:BaseControl
{
}
The point is that when I run the UIVerify tool to see if my controls are generating the correct structure I can not see them:
Another point is, that if I modify the GetChild method from AccessibleForm class in this way:
public override AccessibleObject GetChild(int index)
{
return new AccessibleObject();
////return this.form1.MyContainer.Controls[index].AccessibilityObject;
}
I can see a node on the UIVerify:
But modifying the GetChild method to return a custom accessible object it shows me nothing.
Why are not my controls on the tree?
I do not know what I am missing.
Override Name,value,Role in AccessibleForm class

How to change ItemsSource if the ObservableCollection in another class?

I want to change my ItemsSource but i set the ObservableCollection in another class. How can i Add something to my ItemsSource if the ObservableCollection isn't there?
in Edit window :
private void manageLayout_Click(object sender, Telerik.Windows.RadRoutedEventArgs e)
{
...
scheduleDetail = assetListClass.GetScheduleDetail(xmlScheduleDetail);
ObservableCollection<LibraryData> scheduleDetailOC = new ObservableCollection<LibraryData>(scheduleDetail);
ManageLayout manageLayoutWin = new ManageLayout();
this.Close();
manageLayoutWin.Show();
manageLayoutWin.ManageLayout_GridView.ItemsSource = scheduleDetailOC;
...
}
if it's in the same class i can just use this code :
scheduleDetailOC.Add(abc);
but what if it's in another class? What should i do in ManageLayout window to change the ItemsSource? i tried this :
ManageLayout_GridView.Items.Add(abc);
and i've got an error :
Operation is not valid while ItemsSource is in use. Access and modify
elements with ItemsControl.ItemsSource instead
First of all you need to store the reference to your collection in your class field/property. To have an access from another classes this property should be public.
public class FirstClass
{
public ObservableCollection<LibraryData> ScheduleDetails { get; private set; }
private void manageLayout_Click(object sender, Telerik.Windows.RadRoutedEventArgs e)
{
...
scheduleDetail = assetListClass.GetScheduleDetail(xmlScheduleDetail);
ScheduleDetails = new ObservableCollection<LibraryData>(scheduleDetail);
ManageLayout manageLayoutWin = new ManageLayout();
this.Close();
manageLayoutWin.Show();
manageLayoutWin.ManageLayout_GridView.ItemsSource = ScheduleDetails;
...
}
}
Now you can manipulate collection by reference to the first class. You can use Dependency injection to save a refernce. If your second class needs to add elements to the first class the simplest way is to take a constructor argument:
public class AnotherClass
{
private readonly FirstClass collectionHolder;
public AnotherClass(FirstClass collectionHolder)
{
this.collectionHolder = collectionHolder;
}
public void AddElement()
{
var newElement = GetNewElement(); // creates element that will be add to the collection
collectionHolder.ScheduleDetails.Add(newElement);
}
}
It will works but not good because now AnotherClass knows all about FirstClass public interface. The other reason is that all classes that have reference to the FirstClass can manipulate public collection.
The good design is to create new interface for your FirstClass that will contains only allowed operations and use it in AnotherClass.
public interface IScheduleDetailsCollectionHolder
{
void AddElement(LibraryData data);
}
public class FirstClass : IScheduleDetailsCollectionHolder
{
private ObservableCollection<LibraryData> scheduleDetails;
private void manageLayout_Click(object sender, Telerik.Windows.RadRoutedEventArgs e)
{
...
scheduleDetail = assetListClass.GetScheduleDetail(xmlScheduleDetail);
scheduleDetails = new ObservableCollection<LibraryData>(scheduleDetail);
ManageLayout manageLayoutWin = new ManageLayout();
this.Close();
manageLayoutWin.Show();
manageLayoutWin.ManageLayout_GridView.ItemsSource = scheduleDetails;
...
}
public void AddElement(LibraryData data)
{
scheduleDetails.Add(data);
}
}
public class AnotherClass
{
private readonly IScheduleDetailsCollectionHolder collectionHolder;
public AnotherClass(IScheduleDetailsCollectionHolder collectionHolder)
{
this.collectionHolder = collectionHolder;
}
public void AddElement()
{
var newElement = GetNewElement(); // creates element that will be add to the collection
collectionHolder.AddElement(newElement);
}
}
The other advice is to use MVVM pattern and data binding that is standard de facto for WPF applications.

Why overriden ToString() do not return what I want when item added to ComboBox?

public partial class TestConrol : UserControl
{
public TestConrol()
{
InitializeComponent();
}
public override string ToString()
{
return "asd";
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
TestConrol tc1 = new TestConrol();
comboBox1.Items.Add(tc1);
TestConrol tc2 = new TestConrol();
comboBox1.Items.Add(tc2);
}
}
When form loaded, I see combobox has two items with empty names, instead of "asd" :/
But this work if I override ToString() in common class, not derived from anything:
public class TestClass
{
public override string ToString()
{
return "bla bla bla";
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
TestClass tcl = new TestClass();
comboBox1.Items.Add(tcl);
}
}
After that I see in combobox "bla bla bla"
Create a property in you control and map the DisplayMember of the combobox to that property, it should work.
I tried to understand the source code(!). This is not a simple call to ToString().
There's an internal class System.Windows.Forms.Formatter doing some stuff. It eventually creates a converter. This is roughly equivalent to saying:
var conv = System.ComponentModel.TypeDescriptor.GetConverter(tc1.GetType());
where tc1 is the TestContol from your question. Now, had we used the TestClass tcl which doesn't implement any interfaces, this would have given us a converter which would eventually call ToString().
But in this example we use tc1, and it is a System.ComponentModel.IComponent. Our conv therefore becomes a System.ComponentModel.ComponentConverter. It uses the Site of the IComponent. When we say:
string result = conv.ConvertTo(tc1, typeof(string));
and the Site is null, we get the empty string "" you saw in your combo box. Had there been a Site it would have used its Name instead.
To demonstrate that, put the following into your TestControl instance constructor:
public TestConrol()
{
InitializeComponent();
Site = new DummySite(); // note: Site is public, so you can also
// write to it from outside the class.
// It is also virtual, so you can override
// its getter and setter.
}
where DummySite is something like:
class DummySite : ISite
{
public IComponent Component
{
get { throw new NotImplementedException(); }
}
public IContainer Container
{
get { throw new NotImplementedException(); }
}
public bool DesignMode
{
get { throw new NotImplementedException(); }
}
public string Name
{
get
{
return "asd"; // HERE'S YOUR TEXT
}
set
{
throw new NotImplementedException();
}
}
public object GetService(Type serviceType)
{
return null;
}
}
Use comboBox1.Items.Add(tc1.ToString()); instead of comboBox1.Items.Add(tcl);
This worked for me:
comboBox1.FormattingEnabled = false
In your UserControl, add a property, and call it FriendlyName for example, as such
namespace project
{
public partial class CustomUserControl : UserControl
{
public CustomUserControl()
{
InitializeComponent();
}
public String FriendlyName { get => "Custom name"; }
}
}
And then set the DisplayMember property of your ComboBox to "FriendlyName", as such
myComboBox.DisplayMember = "FriendlyName";
And to me, this was a very clean solution and gut tells me it is the intended way to do it.

Categories

Resources