Reputation: 173
Hi :) I’m just learning C# and WPF and I need to write a tool that:
My thought was a dictionary would be best. The data is only loaded once from file, and changes are only made with the WPF controls. I’ve tried many things but I still keep hitting road blocks when I bind data to the controls. I’ve been working with a simplified version of the tool – below. The data binds to the WPF control – but there is no change event to update the dictionary. I haven’t found a good example to follow. Could someone explain how to get the dictionary to update? And is the strategy the right one? - using a dictionary -using DataContext. If you'd like to see the full project and UI - there is a link at the bottom. I've been working many-many days...with progress but I'm way too slow ;) Cheers Danny
MainWindow.xaml
<Window x:Class="configGen.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="150">
<StackPanel Margin="20" Width="80">
<TextBox Text="{Binding [item1]}" />
<TextBlock Text="{Binding [item1]}" />
<TextBox Text="{Binding [item2]}" />
<TextBlock Text="{Binding [item2]}" />
<TextBox Text="{Binding [item3]}" />
<TextBlock Text="{Binding [item3]}" />
<Slider Value="{Binding [item4]}" Minimum="0" Maximum="256" />
<TextBlock Text="{Binding [item4]}" />
</StackPanel>
MainWindow.xaml.cs
namespace configGen
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
dataClass record = new dataClass();
DataContext = record.generate();
}
}
public class dataClass
{
public Dictionary<string, int> generate()
{
Dictionary<string, int> _data = new Dictionary<string, int>();
_data.Add("item1", 100);
_data.Add("item2", 120);
_data.Add("item3", 140);
_data.Add("item4", 160);
return _data;
}
}
}
Link to full project...
http://www.baytower.ca/btsRV7config.zip
Thanks for all the great feedback everyone!! I will set back to work :)
Upvotes: 1
Views: 8372
Reputation: 173
I've tried tsell's example, using his class in a list. The list is just a convenient way to generate and manage a fixed number of elements. The Item1 WPF control binds to the Item1 object and its value. The object is found by its index number. The binding and dataContext in this case is simple enough for me to use (as a beginner). It works, but I'm not sure it's exactly an elegant way to do it.
public MainWindow()
{
MyObjects = new List<MyObject>();
MyObject item1 = new MyObject();
item1.MyValue = string.Format("100");
MyObjects.Add(item1);
MyObject item2 = new MyObject();
item2.MyValue = string.Format("120");
MyObjects.Add(item2);
MyObject item3 = new MyObject();
item3.MyValue = string.Format("140");
MyObjects.Add(item3);
MyObject item4 = new MyObject();
item4.MyValue = string.Format("160");
MyObjects.Add(item4);
InitializeComponent();
DataContext = this;
}
xaml
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel Margin="20" Width="80">
<TextBox Text="{Binding MyObjects[0].MyValue}" />
<TextBlock Text="{Binding MyObjects[0].MyValue}" />
<TextBox Text="{Binding MyObjects[1].MyValue}" />
<TextBlock Text="{Binding MyObjects[1].MyValue}" />
<TextBox Text="{Binding MyObjects[2].MyValue}" />
<TextBlock Text="{Binding MyObjects[2].MyValue}" />
<Slider Value="{Binding MyObjects[3].MyValue}" Minimum="0" Maximum="256" />
<TextBlock Text="{Binding MyObjects[3].MyValue}" />
</StackPanel>
</Window>
BTW, I will change to int for MyValues..they are all int numbers. For now it is a string.
Upvotes: 0
Reputation: 2771
Here is an example of how I would do it.
Main Class (Code Behind)
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private List<MyObject> _myObjects;
public List<MyObject> MyObjects
{
get { return _myObjects; }
set
{
_myObjects = value;
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("MyObjects"));
}
}
}
public MainWindow()
{
MyObjects = new List<MyObject>();
// Add 20 records for sample data
for (int i = 0; i < 20; i++)
{
MyObject o = new MyObject();
o.Label = string.Format("Key{0}", i);
o.MyValue = string.Format("Value{0}", i);
MyObjects.Add(o);
}
InitializeComponent();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
}
Secondary Class
public class MyObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _label;
public string Label
{
get { return _label; }
set
{
_label = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Label"));
}
}
}
private string _myValue;
public string MyValue
{
get
{
return _myValue;
}
set
{
_myValue = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("MyValue"));
}
}
}
}
XAML File
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="listboxstyle">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Label}" />
<TextBox Grid.Column="1" Text="{Binding Path=MyValue}" />
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox
ItemsSource="{Binding Path=MyObjects}"
ItemTemplate="{StaticResource listboxstyle}"
/>
</Grid>
</Window>
Upvotes: 1
Reputation: 1342
A dictionary is definitly not a convenient way to do a two way data binding in WPF. It seems an ObservableCollection
is more suited to your requirements.
Something like:
public class ItemsList : ObservableCollection<Item>
{
public ItemsList() : base()
{
Add(new Item("item 1", 100));
Add(new Item("item 2", 120));
Add(new Item("item 3", 140));
Add(new Item("item 4", 160));
}
}
Item
is a simple class with a name and a value properties. I have ommitted it here because it is self explanatory.
The advantage here is that you can bind to a dynamic number of items not only the ones declared imperatively.
Once you bind you datacontext to it, you get the automatic property notification for two way databinding.
Your XAML will have to change to accomodate binding to a collection of course. Maybe an ItemsControl
that takes that collection as its ItemsSource
.
Upvotes: 1
Reputation: 15413
Instead of using a Dictionary as your DataContext
I'd create a custom object like MainViewModel
. Give it properties that correspond to item1, item2, etc, except give them appropriate names. Then use <TextBox Text="{Binding MyPropertyName}" />
. To handle updates, you can either set your DataContext
to a new MainViewModel
object or you can set up your class to broadcast property changes. You can do that either through INotifyPropertyChanged on the class or with dependency properties.
At least that's what it seems like you're trying to accomplish. If you're going for displaying an arbitrary number of controls you'd need something different.
Upvotes: 1