I have a view model that is used to produce items for an ItemsControl:
public class PermissionItemViewModel
{
public PermissionsEnum Permission {get; set;}
}
The enum itself is defined in a way that the list is not naturally in alphabetical order, and when the control renders, the items are in the same order as the enum definition.
In my view, I am trying to define a CollectionViewSource and make it sort those items by what would essentially be myEnumValue.ToString(). Instead, the sort seems to be in order of the enum values instead of (what I thought would be) an implicit ToString() result.
<CollectionViewSource x:Key="permissionsViewSource"
Source="{Binding PermissionItems}"> <!-- ObservableCollection<PermissionItemViewModel> -->
<CollectionViewSource.SortDescriptions>
<cm:SortDescription PropertyName="Permission" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
Ultimately, I use the source like this:
<ItemsControl ItemsSource="{Binding Source={StaticResource permissionsViewSource}}" />
I would like it to sort by the names of the enums, not their values. How can I force it to do that?
Looks like you may be able to grab the ListCollectionView and do a custom sort on that.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
CollectionViewSource source = (CollectionViewSource)(this.Resources["MyCollectionViewSource1"]);
ListCollectionView view = (ListCollectionView)source.View;
view.CustomSort = new CustomSorter();
}
public class CustomSorter : IComparer
{
public int Compare(object x, object y)
{
var itemx = x as PermissionItemViewModel;
var itemy = y as PermissionItemViewModel;
return $"{itemx.Permission}".CompareTo($"{itemy.Permission}");
}
}
You could salve your conscience a bit by writing this as an attached behavior for CollectionViewSource that would take a property name for its value. Offhand I think you'd want that to set up a property changed handler on View -- I think I wrote such a thing once, let's see if I can exhume 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?
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;
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.
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!).
Assume the following:
Public ObservableCollection<string> SomeCollection;
Public ObservableCollection<string> SomeOtherCollection
{
get{ return SomeCollection; }
set{ SomeCollection = value; }
}
Both are bound to a ListBox in a WPF UI using
<ListBox ItemsSource="{Binding SomeCollection}" />
<ListBox ItemsSource="{Binding SomeOtherCollection} />
Assuming this, will both update automatically? To my understanding, the first will, but I know absolutely nothing as to the functionality of the second.
Note: This is a rough example. Please don't be hyperliteral with me. :)
The first example will not be able to bind to the UI (only properties may be bound to the UI in wpf), however if you changed it to:
Public ObservableCollection<string> SomeCollection { get; set; }
They would be equivalent. The get; set; is just shorthand for your second example. If you ever want to add any extra logic in either the getter or the setter (like implementing INotifyPropertyChanged), you would have to write out the whole thing. If you just want the simple get and set, the above is the same thing.