I'm working on a small form app, and I have "paired" my radio buttons with lists in a common class. The purpose of this was to turn on/off the corresponding list
public class myType
{
public RadioButton button { get; set; }
public ListBox list { get; set; }
}
I proceed to create these pairs through a for loop inside an array
for (int i = 0; i < broj_botuna; i++)
{
theArray[i] = new myType();
}
I use a common event handler for all the radio buttons:
private void test_CheckedChanged(object sender, EventArgs e)
{
var xx = sender as RadioButton;
//do stuff
positionInArray = Array.IndexOf(theArray, xx);
}
except that the last line of code "xx" should be of type "myType" and not "radioButton" that I managed to retrieve.
So could anyone tell me how do I get the reference from "radioButton" to "myType"? Or is there a better alternative?
You can use Array.FindIndex like:
var positionInArray = Array.FindIndex(theArray, b => b.button == xx);
You could create some constructs that allow you to easily associate your properties to the parent object if you wanted to.
This approach would allow you to always reference your parent type provided that you added a bit more code in your get's and set's.
static void Main()
{
myType item = new myType();
var button = new Button();
myType.button = button;
var list = new ListBox();
myType.list = list;
item = list.GetParent();
bool isSameButton = button == item.button;
bool isSameList = list == item.list;
Assert.IsTrue(isSameButton);
Assert.IsTrue(isSameList);
}
public class myType
{
private RadioButton _button;
public RadioButton button
{
get { return _button; }
set {
value.AssociateParent(this);
_button = value;
}
}
private ListBox _list;
public ListBox list
{
get { return _list; }
set {
value.AssociateParent(this);
_list= value;
}
}
}
public static class Extensions
{
private static Dictionary<object, object> Items { get; set; }
static Extensions()
{
Items = new Dictionary<object, object>();
}
public static void AssociateParent(this object child, object parent)
{
Items[child] = parent;
}
public static object GetParent(this object child)
{
if (Items.ContainsKey(child)) return Items[child];
return null;
}
}
Related
I have an ObservableCollection which getting filled from a TcpClient. When new data arrives (new Items are added), I want to create new Buttons inside an ItemsControl. It works the old way (with CollectionChanged) but I don't get it work with ReactiveUI.
I'm very new to ReactiveUI, and its quite hard for me to getting started. Could you may help me by putting me on the right path or maybe by providing some sample code?
The Idea:
public class ChooseMachineViewModel : ReactiveObject
{
public ReactiveList<Button> ButtonList { get; set; }
private Dictionary<ushort, Button> addressToButton;
//This one is normaly in another class and will be filled by a TcpClient
public readonly ObservableCollection<WWSS.Message.CUStatus> ControlUnitsStatus;
public ChooseMachineViewModel()
{
//TODO: Make this Reactive!
//The ButtonList for an ItemsControl
ButtonList = new ReactiveList<Button>();
//The Dictonary for addresses -> Button
addressToButton = new Dictionary<ushort, Button>();
//The ObservableCollection filled by a TCP Server
ControlUnitsStatus.CollectionChanged += ControlUnitsStatus_CollectionChanged;
}
private void ControlUnitsStatus_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (WWSS.Message.CUStatus stat in e.NewItems)
{
TryAddButton(stat);//When new Status arrive, try to create new button
}
}
if (e.OldItems != null)
{
foreach (WWSS.Message.CUStatus stat in e.OldItems)
{
TryRemoveButton(stat);//When Status removed, try to remove the button
}
}
}
private bool TryAddButton(WWSS.Message.CUStatus status)
{
if (!addressToButton.ContainsKey(status.Address))//if the Address is already in the dictonary don't create a button
{
var but = new Button { Content = status.Address.ToString() };
addressToButton.Add(status.Address, but);
ButtonList.Add(but);
return true;
}
return false;
}
private void TryRemoveButton(WWSS.Message.CUStatus status)
{
if (addressToButton.ContainsKey(status.Address))
{
ButtonList.Remove(addressToButton[status.Address]);
addressToButton.Remove(status.Address);
}
}
}
The trick was to use CreateDerivedCollection:
public class ChooseMachineViewModel : ReactiveObject
{
public IReactiveDerivedList<Button> ButtonList { get; set; }
public ChooseMachineViewModel(ObservableCollection<CUStatus> source)
{
addressToButton = new Dictionary<ushort, Button>();
ButtonList = ControlUnitsStatus.CreateDerivedCollection(status => new Button { Content = status.Address.ToString() },
status => !ButtonList.Any(button => button.Content.ToString().Equals(status.Address.ToString())));
}
}
I need to create a user control MyTypeListControl to display collection of objects of type MyType using a user controls MyTypeDisplayControl instance for each of those objects.
So that I could
add instance of MyTypeListControl to my WinForm, then
load collection of MyType and
assign it to MyTypeListControl's DataSource.
In the result it should generate and show appropriate count of MyTypeDisplayControl instances in MyTypeListControl's instance.
In case if I needed to show list of properties - equivalent would be DataGrid with specific fields from MyType assigned to specific DataGrid's columns, but I want to view each MyType item as a user control - with more power for visual representation and functionality than DataGrid provides for it's rows.
Is that even possible?
I found this SO resource how to create My collection type, but this is only small part of the problem solution...
It is quite easy (if you know how) and doesn't take so much effort as you might think in the first place (at least for a simple implementation that handles collection of less then 100 items).
So at first lets create a MyType:
public class MyType
{
public static MyType Empty = new MyType(String.Empty, DateTime.MinValue);
public MyType(string myName, DateTime myBirthday)
{
MyName = myName;
MyBirthday = myBirthday;
}
public DateTime MyBirthday { get; private set; }
public string MyName { get; private set; }
}
At next we need a MyTypeControl:
public partial class MyTypeControl : UserControl
{
private MyType _MyType;
private Label labelBirthday;
private Label labelName;
private Label labelSeparator;
public MyTypeControl()
{
InitializeComponent();
}
public event EventHandler MyTypeChanged;
public MyType MyType
{
get { return _MyType; }
set
{
if (_MyType == value)
return;
_MyType = value ?? MyType.Empty;
OnMyTypeChanged(EventArgs.Empty);
}
}
protected virtual void OnMyTypeChanged(EventArgs eventArgs)
{
UpdateVisualization();
RaiseEvent(MyTypeChanged, eventArgs);
}
protected void UpdateVisualization()
{
SuspendLayout();
labelName.Text = _MyType.MyName;
labelBirthday.Text = _MyType.MyBirthday.ToString("F");
labelBirthday.Visible = _MyType.MyBirthday != DateTime.MinValue;
ResumeLayout();
}
private void InitializeComponent()
{
labelName = new Label();
labelBirthday = new Label();
labelSeparator = new Label();
SuspendLayout();
labelName.Dock = DockStyle.Top;
labelName.Location = new Point(0, 0);
labelName.TextAlign = ContentAlignment.MiddleCenter;
labelBirthday.Dock = DockStyle.Top;
labelBirthday.TextAlign = ContentAlignment.MiddleCenter;
labelSeparator.BorderStyle = BorderStyle.Fixed3D;
labelSeparator.Dock = DockStyle.Top;
labelSeparator.Size = new Size(150, 2);
Controls.Add(labelSeparator);
Controls.Add(labelBirthday);
Controls.Add(labelName);
MinimumSize = new Size(0, 48);
Name = "MyTypeControl";
Size = new Size(150, 48);
ResumeLayout(false);
}
private void RaiseEvent(EventHandler eventHandler, EventArgs eventArgs)
{
var temp = eventHandler;
if (temp != null)
temp(this, eventArgs);
}
}
Then comes our magically list control:
public class MyTypeListControl : UserControl
{
private ObservableCollection<MyType> _Items;
public MyTypeListControl()
{
AutoScroll = true;
_Items = new ObservableCollection<MyType>();
_Items.CollectionChanged += OnItemsCollectionChanged;
}
public Collection<MyType> Items
{
get { return _Items; }
}
private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdateVisualization();
}
private void UpdateVisualization()
{
SuspendLayout();
Controls.Clear();
foreach (var item in _Items)
{
var control = new MyTypeControl { MyType = item, Dock = DockStyle.Top };
Controls.Add(control);
Controls.SetChildIndex(control, 0);
}
ResumeLayout();
}
}
And now simply create the list control in your form or parent control and fill it with some meaningful values:
myTypeListControl.Items.Add(new MyType("Adam", DateTime.UtcNow.Add(-TimeSpan.FromDays(365 * 40))));
myTypeListControl.Items.Add(new MyType("Eva", DateTime.UtcNow.Add(-TimeSpan.FromDays(365 * 38))));
I have a user control that inherited from Combobox control. I want to bind data in the constructor of the user control. but when I add it to form and run the project it shows duplicated items.
When I add control to my winform its add items in the Designer file of form and when I run project it added again in constructor of user control.
public partial class CheckSeriesBox : ComboBox
{
private static List<string> CheckSeries;
public CheckSeriesBox()
{
InitializeComponent();
CheckSeries = new List<string>();
SetCheckSeries();
this.Items.AddRange(CheckSeries.ToArray());
this.SelectedIndex = 0;
}
public static List<string> SetCheckSeries()
{
CheckSeries.Add("A");
CheckSeries.Add("B");
}
}
http://social.msdn.microsoft.com/forums/vstudio/en-US/3e35b534-7d3f-4832-8859-b5cb838bd62a/extended-combobox-adds-items-twice
public partial class CheckSeriesBox : ComboBox
{
private static List<string> CheckSeries;
public CheckSeriesBox()
{
InitializeComponent();
CheckSeries = new List<string>();
SetCheckSeries();
if (DesignMode)
{
this.Items.AddRange(CheckSeries.ToArray());
}
}
public static List<string> SetCheckSeries()
{
CheckSeries.Add("A");
CheckSeries.Add("B");
}
protected new bool DesignMode
{
get
{
if (base.DesignMode)
{
return true;
}
else
{
Control parent = this.Parent;
while ((parent != null))
{
System.ComponentModel.ISite site = parent.Site;
if ((site != null) && site.DesignMode)
{
return true;
}
parent = parent.Parent;
}
return false;
}
}
}
}
I have a collection of panels which are highlighted when user clicks on them. I want to force them to behave as a set of radio buttons so only the one that is clicked on is highlighted and others aren't.
I guess that there must be a way to manipulate whole collection (set property to false) from the inside, because the event is triggered by one item from the collection. Is there a way for the one item to manipulate whole collection? This is such a common feature in applications so I guess there must be a pattern how to do it properly. Thanks.
You may store collection of your panels and handle required functionality as in following code snippet:
List<Panel> Panels;
private void Initialization()
{
Panels = new List<Panel>();
Panels.Add(pnl1);
Panels.Add(pnl2);
//add all your panels into collection
foreach(Panel Item in this.Panels)
{
//add handle to panel on click event
Item.Click += OnPanelClick;
}
}
private void OnPanelClick(object sender, EventArgs e)
{
foreach(Panel Item in this.Panels)
{
//remove highlight from your panels, real property should have other name than Panel.HighlightEnabled
Item.HighlightEnabled = false;
}
((Panel)sender).HighlightEnabled = true; //add highlight to Panel which invoked Click event
Application.DoEvents(); //ensure that graphics redraw is completed immediately
}
private void AddNewPanelIntoLocalCollection(Panel panel)
{
//here you can add new items to collection during program lifecycle
panel.Click += OnPanelClick;
this.Panels.Add(panel);
}
This is how I do it
public class SelectOne : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private bool isSelected = false;
private HashSet<SelectOne> selecteOnes = null;
public bool IsSelected
{
get { return isSelected; }
set
{
if (isSelected == value) return;
if (isSelected && selecteOnes != null)
{
foreach (SelectOne so in selecteOnes)
{
if (so == this) continue;
so.IsSelected = false;
}
}
NotifyPropertyChanged("IsSelected");
}
}
public SelectOne() { }
public SelectOne(bool IsSelected) { isSelected = IsSelected; }
public SelectedOne(bool IsSelected, HashSet<SelectOne> SelecteOnes)
{
isSelected = IsSelected;
selecteOnes = SelecteOnes;
}
}
Eventually I did find a way to do this properly with only one delegate.
In class A I have a collection of objects B
List<B> b = new List<B>
class B, needs to have an unique ID and delegete for void metod with Id parameter
delegate void DeleteItemDelegate(int id);
class B
{
public int ID {get; set;}
public DeleteItemDeleate deleteThis {set; get;}
}
class A has a metod like this:
public void RemoveItem(int id)
{
for (int x = 0; x < b.Count; x++)
{
if (b[x].id == id)
{
b.RemoveAt(x);
}
}
}
when adding a new B object into List just add metod RemoveItem to B.deleteThis delegate
B bObject = new B();
bObject.deleteThis = RemoveItem;
b.Add(bObject);
Now all you need to do is add DeleteMe metod in B class
void DeleteMe()
{
// and call local delegate - pointing to metod which actually can manipulate the collection
deleteThis(id);
}
I try to run some code when collection is changed. I keep collection as property in Data class:
public static ObservableCollection<OfferedConfiguration> DeviceAdjustedConfigurations
{
get { return deviceAdjustedConfigurations; }
set { deviceAdjustedConfigurations = value; }
}
and register it in code like that:
Data.DeviceAdjustedConfigurations.CollectionChanged += new NotifyCollectionChangedEventHandler(DeviceAdjustedConfigurationsCollectionChanged);
But after registration CollectionChanged is null and the appropriate code in delegated method is not run. In this place DeviceAdjustedConiguration already contains some data. What am I doing wrong?
You should avoid having a set property accessor for collection types, one reason being the one you experienced here with events. Another problem is if someone caches the collection and adds items to it later.
var old = obj.DeviceAdjustedConfigurations;
obj.DeviceAdjustedConfigurations = new ObservableCollection<OfferedConfiguration>();
old.Add(new OfferedConfiguration()); // what should happen here?
instead, remove the set-accessor and use the existing collection directly.
obj.DeviceAdjustedConfigurations.Add(new OfferedConfiguration());
If you really need to set the collection, you need to handle this with for instance a property change event from the class that owns the DeviceAdjustedConfigurations.
public class Item
{
public static ObservableCollection<OfferedConfiguration> DeviceAdjustedConfigurations
{
get { return deviceAdjustedConfigurations; }
set
{
if (deviceAdjustedConfigurations != value)
{
onDeviceConfigurationsChanging(deviceAdjustedConfigurations, value);
deviceAdjustedConfigurations = value;
}
}
}
public static event EventHandler<ConfigurationChangedEventArgs> DeviceConfigurationsChanging;
private static void onDeviceConfigurationsChanging(
ObservableCollection<OfferedConfiguration> oldList,
ObservableCollection<OfferedConfiguration> newList)
{
var handler = DeviceConfigurationsChanging;
if (handler != null)
{
handler(null, new ConfigurationChangedEventArgs(oldList, newList));
}
}
}
public class ConfigurationChangedEventArgs : EventArgs
{
public ConfigurationChangedEventArgs(
ObservableCollection<OfferedConfiguration> oldList,
ObservableCollection<OfferedConfiguration> newList)
{
OldList = oldList;
NewList = newList;
}
public ObservableCollection<OfferedConfiguration> OldList { get; private set; }
public ObservableCollection<OfferedConfiguration> NewList { get; private set; }
}
public class Consumer
{
public void foo()
{
Item.DeviceConfigurationsChanging += updateEvents;
}
private void updateEvents(object sender, ConfigurationChangedEventArgs args)
{
args.OldList.CollectionChanged -= onCollectionChanged;
args.NewList.CollectionChanged += onCollectionChanged;
}
private void onCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) { }
}