WPF Check box command to List or From List - c#

I am new to WPF and not sure whether, whatever i am trying to do is possible or not.
I have checkbox as below :-
<CheckBox Name="cbAccess" Checked="cbAccess_CheckedChanged" Unchecked="cbAccess_CheckedChanged">Access To Save</CheckBox>
List as below :-
List<int> accessList = new List<int>();
if checkbox is checked then it should fire command 10 (int) and i will add the 10 in some list. and then unchecked then remove from list.
When first time window get loaded then checkbox will be checked or unchecked on the basis of commnad 10 exist in list or not.

CheckBox derives from ButtonBase so you automatically get access to a Command and CommandParameter. (MSDN) Using those, you could conceivable do something like:
<CheckBox Command="{Binding ToggleItemInListCommand}" CommandParameter="10"/>
and have the command function:
private void ToggleItemInList(object param)
{
int item;
if (int.TryParse(param.ToString(), out item))
{
//If its actually an int
if (myList.Contains(item))
myList.Remove(item);
else
myList.Add(item);
}
}
Exposing that method via an ICommand is trivial and left to you. While such a solution would work, it doesn't solve the problem of getting the initial state into your CheckBox.
'
Given that you want that behavior as well, I would not use commands at all here, instead using IsChecked with a converter. Unfortunately, you'll need to get the collection into the converter on a DependencyProperty to make it work. The XAML would look something like:
<!-- Resources -->
<local:ListContainsConverter x:Key="ListContainsConverter" Collection="{Binding MyList}"/>
<!-- Main Section -->
<CheckBox IsChecked="{Binding Path=., Converter={StaticResource ListContainsConverter}, ConverterParameter=10}"/>
Where ListContainsConverter is defined:
public class ListContainsConverter : DependencyObject, IValueConverter
{
public object Convert(...)
{
//More generic version left as an exercise
int testValue = int.Parse(parameter.ToString());
return collection.Contains(testValue);
}
public object ConvertBack(...)
{
bool checked = (bool)value;
int testValue = int.Parse(parameter.ToString());
if (checked)
Collection.Add(testValue);
else
Collection.Remove(testValue)
}
public IList<int> Collection
{
get { return (IList<int>)GetValue(CollectionProperty); }
set { SetValue(CollectionProperty, value); }
}
public static readonly DependencyProperty CollectionProperty =
DependencyProperty.Register("Collection", typeof(IList<int>), typeof(ListContainsConverter), null);
}
This approach is really far better, as it is far easier to keep your state maintained. You are ignoring the value piece of the converter, which is a bit odd, but I would take the trade off here.

Related

Trigger PropertyChanged of the certain element in collection

Having two collections in the ViewModel, one is working as a source of rows for the DataGrid (in this case have only values 1,2,3,..), and other object that represents sequencer for one column in the DataGrid (based on ID expose some value in property).
In the example below I have used default ObservableObject and FullyObservableCollection behaviour, but it was also tried with other kinds of collections with no success. WPF behavior was tested on both DataGridComboBoxColumn and DataGridTemplateColumn with ComboBox.
ViewModel:
public class ViewModel : ObservableObject
{
private FullyObservableCollection<Seq> mSequencer;
public FullyObservableCollection<Seq> Sequencer
{
get { return mSequencer; }
set { SetProperty(ref mSequencer, value); }
}
private FullyObservableCollection<RowSrc> mRowSource;
public FullyObservableCollection<RowSrc> RowSource
{
get { return mRowSource; }
set { SetProperty(ref mRowSource, value); }
}
}
Class definitions:
public class Seq : ObservableObject
{
private int mId;
public int Id
{
get { return mId; }
set { SetProperty(ref mId, value); }
}
private string mName;
public string Name
{
get { return mName; }
set { SetProperty(ref mName, value); }
}
}
public class RowSrc : ObservableObject
{
private int mValue;
public int Value
{
get { return this.mValue; }
set { SetProperty(ref mValue, value); }
}
}
View - XAML:
<CollectionViewSource x:Key="Proxy" Source="{Binding Sequencer}"/>
<DataGrid ItemsSource="{Binding RowSource}">
<DataGrid.Columns>
<DataGridComboBoxColumn
ItemsSource="{Binding Source={StaticResource Proxy}"
SelectedValueBinding="{Binding Value}"
SelectedValuePath="Id"
DisplayMemberPath="Name"/>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
From the above, datagrid is initiliazed with the values of RowSource, that will go throught binding tunnel to the Proxy, which will lead to Sequencer. Based on the Value of each row, object with same Id will be returned from the Sequencer collection.
As is, on the first start everything works well. Problem starts, when we want to empty the Sequencer and fill it again.
Sequencer.Clear();
Sequencer.AddRange(...);
Now the items in the Sequencer refresh, however the binding between Row[n].Combobox and the items in Sequencer DO NOT REFRESH. The reason behind this is that the properties themselves has not changed, only items in collection.
Workaround - hack:
public class RowSrc
{
...
public void InvokeChange()
{
OnPropertyChanged(nameof(Value));
}
}
public class ViewModel
{
Sequencer.Clear();
Sequencer = GenerateNewCollection();
//after update of Sequencer
foreach (var nRowSrc in RowSrc) nRowSrc.InvokeChange();
}
However this means that we would need to add this specific method to each of our Model classes, and then remember to call them on each of Sequencer change.
Q: How to invoke property change automatically, when the collection changed OR how to handle this binding correctly?
For the problem you posted, is very simply, just use an ObsevrableCollection instead of a List. In fact, ObsevrableCollection dose not only report Add and Remove, it report all kind of movement that support by INotifyCollectionChanged.CollectionChanged event. So if you use indexer of ObsevrableCollection to set an element, a CollectionChanged of type NotifyCollectionChangedAction.Replace will be raised, and the binding engine will handle it fine from that. But I am a little confused after read you comment, and can not be sure if this is really what you want.
You can raise the PropertyChanged event for your Property Prop1 through calling MyList[0].OnPropertyChanged(nameof(Foo.Prop1));.
There is no need to create a TriggerPropertyChanged method.
This works, because in your example OnPropertyChanged is public and optionally accepts a string, that describes the property name.

Changing Value of a ComboBox in a DataTemplate

I have a Listbox with a DataTemplate which includes a Combobox. I need to change the selectedItem/Index of a particular ComboBox. How would I access it?
Additional Detail
All the combobox have the same options. If a Combobox is set to the same value as another ComboBox then the Combobox that was set first should return to empty (which is the first item in my cbxOptions Dictionary that the ComboBoxes are Bound to).
<DataTemplate x:Key="lbxHeaderDataTemplate">
<Grid>
<Label Content="{Binding Item1}"></Label>
<ComboBox Grid.Column="1" ItemsSource="{Binding Item2}"
DisplayMemberPath="Key" SelectionChanged="ComboBox_SelectionChanged"></ComboBox>
</Grid>
</DataTemplate>
C#
Populate UI
foreach (DataColumn dc in _loadedData.Columns)
{
ListBox.Items.Add(new Tuple<string, Dictionary<string, string>>
(dc.ColumnName, cbxOptions));
}
Trying to wipe combobox
This is where I would expect I could foreach through the Listbox, checking the controls for a match at which point I'd change it to blank. However my foreach just gives me back stupid Tuple...which is readonly but I don't think that'd update my ComboBox anyways.
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox cbxSelected = (ComboBox)sender;
DependencyObject parent = VisualTreeHelper.GetParent(cbxSelected);
Label currentLbl = null;
foreach (object o in ((Grid)parent).Children)
{
if (o is Label)
{
currentLbl = (Label)o;
}
}
string LblText = currentLbl.Content.ToString();
string cbxValue = cbxSelected.SelectedValue.ToString();
//HERE I want to iterate through the listbox controls, not the datasource
foreach (Tuple<string, Dictionary<string, string>> l in lbxDatFields.Items)
{
//l.Item2 = "";
if (l.Item1.EndsWith(cbxOptions[cbxValue]))
l = new Tuple<string, Dictionary<string, string>>(l.Item1, "");
}
}
I'm sure there must be a very simple way of accessing the control. Any help would be much appreciated. Please let me know if additional info is required.
Without a good Minimal, Complete, and Verifiable code example that clearly and concisely illustrates your question, it's not practical to try to address your current design. Based on the bit of code you did post, one can make some observations though:
Since Tuple<...> is immutable, you can't modify the Item2 property. You have to replace the entire Tuple<...> object with a new one.
The code you posted shouldn't even compile, because you are trying to modify the l variable in the foreach loop.
Even if you could, it wouldn't change the element in the list itself, just that particular variable.
Not that you even want to change the element; it's the selection of the combo box that should change, not its Item2 options.
The use of a dictionary object for the ComboBox items eludes me. Perhaps with a complete code example, it would be more clear.
All that said…
How would I access it?
This question comes up only because you are misusing WPF to start with. You should not be manipulating the UI directly; instead, your UI state should be represented in view model data structures. Then the ComboBox selection would be bound to a view model property, and the answer to your question would be simply to look at that property.
It's hard to know for sure, given the lack of details, but it appears to me that you are trying to implement a scenario where you have a list of items, where each item has a selectable option, and you want those options to be mutually exclusive. That is, only one item at a time can have any given option.
Assuming that's the case, I will show an implementation that in my opinion is much better than the approach you are attempting to implement. That is, it uses the basic idea I've proposed above, where you start with the data models, and then work back to the UI from there. Doing it this way, the data models are very simple and easy to understand, and so is all of the implementation for the behavior you want.
It looks like this…
First, start with the basic per-item view model data structure:
class PropertyChangedExEventArgs<T> : PropertyChangedEventArgs
{
public T OldValue { get; }
public PropertyChangedExEventArgs(string propertyName, T oldValue)
: base(propertyName)
{
OldValue = oldValue;
}
}
class ItemViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _UpdateField(ref _name, value); }
}
private string _value;
public string Value
{
get { return _value; }
set { _UpdateField(ref _value, value); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this,
new PropertyChangedExEventArgs<T>(propertyName, oldValue));
}
}
Notes:
The above class implements INotifyPropertyChanged directly. In a real-world program, this implementation would typically be in a base class, which each view model class inherits. If you do a significant amount of WPF programming, you'll have this base class as a reusable component you just include in each project, either in a separate project you reference, or as a code snippet. There are many WPF user frameworks you can use as well, which provide this functionality.
In this particular example, there's not already a convenient mechanism for event subscribers to know the old value of the property after it's changed, but the logic involved requires that, so that the key for the mapping from value to model object can be removed from the dictionary when it's no longer valid. There are a variety of ways to address that need — arguably, the more straightforward is to just do a linear search of the Values collection of the relatively small dictionary. But I decided to extend the PropertyChangedEventArgs class instead, as that's a more scalable solution to that particular need (and so is more useful as a general solution to the problem).
Here, I only need one class to implement that interface, and it's simpler for the sake of illustration to keep everything together there.
Okay, so with the per-item data structure in place, we also want a parent data structure to encapsulate these items as a collection and to handle the broader manipulation of these items:
class MainViewModel
{
public ObservableCollection<ItemViewModel> Items { get; } =
new ObservableCollection<ItemViewModel>
{
new ItemViewModel { Name = "Item #1" },
new ItemViewModel { Name = "Item #2" },
new ItemViewModel { Name = "Item #3" },
};
public IReadOnlyList<string> Options { get; } =
new [] { "Option One", "Option Two", "Option Three" };
private readonly Dictionary<string, ItemViewModel> _valueToModel =
new Dictionary<string, ItemViewModel>();
public MainViewModel()
{
foreach (ItemViewModel itemModel in Items)
{
itemModel.PropertyChanged += _ItemPropertyChanged;
}
}
private void _ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ItemViewModel.Value))
{
ItemViewModel itemModel = (ItemViewModel)sender;
PropertyChangedExEventArgs<string> exArgs =
(PropertyChangedExEventArgs<string>)e;
if (exArgs.OldValue != null)
{
_valueToModel.Remove(exArgs.OldValue);
}
if (itemModel.Value != null)
{
if (_valueToModel.TryGetValue(
itemModel.Value, out ItemViewModel otherModel))
{
otherModel.Value = null;
}
_valueToModel[itemModel.Value] = itemModel;
}
}
}
}
This object maintains the collection of items, as well as the collection of options for the ComboBox elements. This is also where the logic to handle the mutual-exclusion of options is handled, because this is the class that has access to all of the per-item data objects.
On that last point: you could, of course, provide a way for the per-item objects to interact with the parent data structure to be able to enumerate the other per-item objects. This would allow each per-item object to handle its own property changes, so that the parent object doesn't need to subscribe to each per-item object's PropertyChanged event. But doing so would also increase coupling between the classes and make the basic logic harder to follow. IMHO, it is preferable to keep this top-down approach, where owned objects know as little as possible about their owners (and in this case, nothing at all).
Note that with the above, all of the logic necessary to track the state of the items and ensure mutual exclusion of the options setting is present, without anything that is actually specific to the view objects. The above code would work in any program, with or without a user interface. It's completely decoupled from the view itself.
And so, how does the view use it? Like this:
<Window x:Class="TestSO45196940ComboBoxExclusive.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO45196940ComboBoxExclusive"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:MainViewModel/>
</Window.DataContext>
<StackPanel>
<ListBox ItemsSource="{Binding Items}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type l:ItemViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="{Binding Name}"/>
<ComboBox ItemsSource="{Binding DataContext.Options, RelativeSource={RelativeSource AncestorType=ListBox}}"
SelectedItem="{Binding Value}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListBox.Resources>
</ListBox>
</StackPanel>
</Window>
Similar to how the ItemViewModel object knows nothing about the MainViewModel, but rather the latter subscribes to the former's PropertyChanged event and accesses the item object's properties to do the work, the view binds to the relevant properties of both model objects, without those objects having any need to know about those bindings, or the view itself.
The view has no code-behind at all. It's just a simple, declarative description of what the user sees, and does nothing more than present to the user the current state of the underlying data.
Doing it this way keeps everything very simple and disconnected, so that each object has a very narrow set of responsibilities, and the interaction between objects is kept to a minimum. This makes it easier to assure that the code is correct, and reduces the mental workload when implementing features, because you're only dealing with a small section of the code at a time, instead of having to keep straight how everything relates to each other.
For what it's worth, it took way longer to explain the code above here in this post, than it did to write the code itself. Following the standard WPF idioms, the actual authoring the code can go very quickly, especially if you already have the basic base classes in place for things like INotifyPropertyChanged. Much of that time savings comes from not having to puzzle over how to get at the data you need. By following better practices, the data is always already right there where you want it.
I have a Listbox with a DataTemplate which includes a Combobox. I need to change the selectedItem/Index of a particular ComboBox. How would I access it?
By accessing the corresponding data item in the Items collection of the ListBox.
Replace your Tuple<string, Dictionary<string, string>> with a class that also includes a SelectedIndex property. Make sure that you implement the INotifyPropertyChanged interface correctly:
class DataItem : INotifyPropertyChanged
{
public string Item1 { get; set; }
public Dictionary<string, string> Item2 { get; set; }
private int _selectedIndex;
public int SelectedIndex
{
get { return _selectedIndex; }
set { _selectedIndex = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
...
foreach (DataColumn dc in _loadedData.Columns)
{
ListBox.Items.Add(new DataItem() { Item1 = dc.ColumnName, Item2 = cbxOptions });
}
Then you bind the SelectedIndex property of the ComboBox in your DataTemplate to your SelectedIndex property:
<ComboBox Grid.Column="1" ItemsSource="{Binding Item2}" DisplayMemberPath="Key"
SelectedIndex="{Binding SelectedIndex}"></ComboBox>
And change the selected index of a ComboBox by setting the source property of the corresponding object in the Items collection:
(ListBox.Items[2] as DataItem).SelectedIndex = 1;

Data-binding ObservableCollection<T> with ComboBox doesn't work with Dependency Property

I have a list of objects (ObservableCollection subjectlist) and want to display them in a Combobox via data-binding and dependency property.
WPF Data Binding to a Combo Box
I searched on stackoverflow and tried to implement the solution of Craig Suchanec in the link above. (tried the whole day now and I just don't get what's wrong with my code)
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public static readonly DependencyProperty SubjectListProperty =
DependencyProperty.Register("SubjectList",
typeof(ObservableCollection<Subject>),
typeof(MainWindow));
private ObservableCollection<Subject> subjectList = new ObservableCollection<Subject>();
Initialization init1;
public ObservableCollection<Subject> SubjectList
{
get { return (ObservableCollection<Subject>)GetValue(SubjectListProperty); }
// get { return subjectList; }
}
public MainWindow()
{
init1 = new Initialization();
subjectList = init1.createMenuSubject();
InitializeComponent();
//this.comboBox.DataContext = SubjectList;
}
}
MainWindow.xaml
<Grid>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left"VerticalAlignment="Top" Width="120" Margin="321,10,0,0"
ItemsSource="{Binding ElementName=mainWindow, Path=SubjectList}" DisplayMemberPath="Name"/>
</Grid>
It DOES work if I just set the DataContext and work without dependency property, but as soon as I try to use the dependency property for data-binding it does NOT and I don't see the significant difference between my implementation and the solution given in the link.
It would be much appreciated, if somebody could help me with this problem.
I can't see anywhere in your code where you are actually setting the value of the SubjectList property.
You are however setting the value of subjectList, but you're binding to SubjectList. Note the casing difference.
You should write:
public ObservableCollection<Subject> SubjectList
{
set { base.SetValue(SubjectListProperty, value); }
get { return (ObservableCollection<Subject>)base.GetValue(SubjectListProperty); }
}
instead of
public ObservableCollection<Subject> SubjectList
{
set { base.SetValue(SubjectListProperty, value); }
get { return subjectList; }
}
or any other ad hoc format. You are setting subjectList in your constructor MainWindow(), however, it will not set the value of SubjectList (with Capital S) and a property change event is never raised. Remove subjectList.
If you are wondering why the DataContext approach works, you should note it will work even if you do not use a DepenedencyProperty. However, if you implement INotifyPropertyChange, it will work with setting ElementName too.

Binding to Reversed ObservableCollection from ViewModel

First, I want to say that after several study on 2 different threads (shown below), I decided to post this question since it's quite different.
So, I want to bind an ItemsControl from my view to a property to get a reversed version of a collection.
I have this view (trimmed for clarity) :
<UserControl x:Class="NumberedMusicalScoresWriter.V.NotationGroupV" ...>
...
<Grid>
<ItemsControl ...
ItemsSource="{Binding ReversedNotationVMs, Mode=OneWay}">
...
</ItemsControl>
</Grid>
...
</UserControl>
And, I have this viewmodel (trimmed for clarity) :
public class NotationGroupVM : ...
{
...
public ObservableCollection<NotationVM> ReversedNotationVMs
{
get { return (ObservableCollection<NotationVM>)NotationVMs.Reverse(); //ERROR!! }
}
public ObservableCollection<NotationVM> NotationVMs
{
get { return _notationVMs; }
set { _notationVMs = value; NotifyPropertyChanged("NotationVMs"); NotifyPropertyChanged("ReversedNotationVMs"); }
}
}
But there's this error (See error comment above to spot the problematic line) :
Unable to cast object of type
'd__a01[NumberedMusicalScoresWriter.VM.NotationVM]'
to type
'System.Collections.ObjectModel.ObservableCollection1[NumberedMusicalScoresWriter.VM.NotationVM]'.
I've also tried to apply .ToList<NotationVM>() before reversing, and making a new collection each time the main field got updated. But they didn't work out.
I also need to keep the reversed to be sync-ed with the un-reversed one. NOT just one time reversion only
I've also read an issue about it here and here, but all of them provide either the xaml solution only or i didn't understand them. I need the VM one.
Thanks.
I agree with the comments above that a different approach may give you a better result, but to answer the question as asked:
NotationVMs.Reverse() returns an IEnumerable. You can't cast this directly to an ObservableCollection because, even though ObservableCollection is one implementation of IEnumerable, it happens to not be the implementation that this particular function is returning. You can always cast an ObservableCollection to an IEnumerable, but the opposite is not always true (all squares are rectangles, but not all rectangles are squares).
To return a reversed collection, try this:
public ObservableCollection<NotationVM> ReversedNotationVMs
{
get { return new ObservableCollection<NotationVM>(NotationVMs.Reverse()); }
}
In order to keep this in sync with the NotationVMs collection, you'll need to watch for collection changed events:
public ObservableCollection<NotationVM> NotationVMs
{
get { return _notationVMs; }
set
{
if (_notationVMs != null)
{
_notationVMs.CollectionChanged -= OnNotationVMsCollectionChanged;
}
_notationVMs = value;
if (_notationVMs != null)
{
_notationVMs.CollectionChanged += OnNotationVMsCollectionChanged;
}
NotifyPropertyChanged("NotationVMs");
NotifyPropertyChanged("ReversedNotationVMs");
}
}
private void OnNotationVMsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("ReversedNotationVMs");
}
This will sync changes in your NotationVMs to your ReversedNotationVMs, but not the other way around. Since your binding to ReversedNotationVMs is one way, this should suffice.
just from my mind and maybe not the full answer for you. lets say we have a collection ordered by ID
public OberservableCollection<MyNotation> MySource {get;set;}
then i can create a default view and a Reverse view
public ICollectionView MyViewOrderedByID {get;set;}
public ICollectionView MyViewOrderedByIDReversed {get;set;}
//ctor
this.MyViewOrderedByID = CollectionViewSource.GetDefaultView(this.MySource);//default
this.MyViewOrderedByID.SortDescriptions.Add(new SortDescription("ID", ListSortDirection.Ascending));
this.MyViewOrderedByIDReversed= new CollectionViewSource{ Source=this.MySource}.View;//new one
this.MyViewOrderedByIDReversed.SortDescriptions.Add(new SortDescription("ID", ListSortDirection.Descending));
xaml
<ItemsControl ItemsSource="{Binding MyViewOrderedByID}"/>
<ItemsControl ItemsSource="{Binding MyViewOrderedByIDReversed}"/>
then views changes whenever the Source is changing

"Binding" wpf Combox selectedValue to an integer?

I just started a new wpf project in hopes that I could learn a new technique as opposed to using winForms all the time.
I seem to be having way too much difficulty binding the selected value of a comboBox to an integer variable in my "MainWindow" class.
I have been looking at a host of "simple" examples from sites like codeproject, but they all seem way too complicated to just return the selected value of a comboBox. I am used to setting the "SelectedValueChanged" property and just setting a variable, which takes just a few clicks, like so:
public int value;
public void comboBox_SelectedValueChanged()
{
value = comboBox.SelectedValue();
}
Is there a similarly sweet, simple, and short way to properly "bind" the selected comboBox item to an integer?
I am trying to understand how to use INotifyPropertyChanged but I keep getting errors when I try to use it. This is what I have so far, but to be honest, I'm not sure where I am going with it:
// Combo Box Value
public class ComboValue
{
#region Members
int valueToReturn;
#endregion
# region Properties
public int numWeeks
{
get { return valueToReturn; }
}
#endregion
}
// Veiw Model Class
public class ComboValueViewModel:INotifyPropertyChanged
{
#region Construction
public ComboValueViewModel()
{
}
#endregion
}
and I've never used "#region" before, I have no clue what that is.
Could someone fill me in if I'm headed down the right path here?
You don't mention how much you know of MVVM but here goes. Your view will have an associated ViewModel class. In here you'll expose a property containing the items to bind to the combobox, e.g.:
public List<ComboValue> ComboItems { get; set; }
If you populate this property in the VM's constructor, then a List<> is probably sufficient; however you'll often see an ObservableCollection<> used for this kind of thing - this comes into its own if you need to add or remove items within your VM code - your view will react to such changes and update the list accordingly. This won't happen with a List<>.
As for INotifyPropertyChanged, I haven't implemented this pattern in the above code snippet. Again, it's not strictly necessary if you populate the collection in the VM constructor and won't be re-assigning that property again. However it's good practice to use the INPC pattern on your VM properties. Without it, if you were to reassign that property elsewhere in your code, e.g.:-
ComboItems = aNewListOfItems;
then the view wouldn't be made aware of the property change, and the ComboBox wouldn't update. If you need this to happen then implement the INPC pattern on the property, e.g.:-
public List<ComboValue> ComboItems // or ObservableCollection<ComboValue>
{
get
{
return _comboItems;
}
set
{
if (_comboItems != value)
{
_comboItems = value;
OnPropertyChanged("ComboItems");
}
}
}
As you are working with a ComboBox, your VM should also expose a property that you bind to the control's SelectedItem property. This property should implement INPC, e.g.:-
public ComboValue SelectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem != value)
{
_selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
}
As you select items in the combo, the VM's SelectedItem property will change to reflect the current selection.
Finally, your XAML should end up looking something like this:-
<ComboBox ItemsSource="{Binding ComboItems}" SelectedItem="{Binding SelectedItem}" />
Hope this gives you a little "primer" into WPF binding! (Code snippets taken from memory so may not be 100% correct!).
Edit
Your ComboValue class exposes a numWeeks property. As it stands, you'll probably find that your ComboBox displays a list of ComboValue type names. To get the number to appear, the easiest thing is just to override .ToString() in your class and return the value of numWeeks. For more advanced formatting of items in controls such as this, you'll typically specify an ItemTemplate (again, plenty of examples can be found via Google!).

Categories

Resources