I've got a dilemma here. So I have mutiple expanders stacked on top of one another. Inside each expander is a ListBox, data bound, where each listitem displays a name of an object.
I've bound the search to filter the list items based on their name. However since I have two observable objects, the filtered items and unfiltered, the UI doesn't appear to get populated until someone searches. Whats the best way to fix this. I find it redundant to add items to both lists each time a new Person gets created. Using an mvvm approach.
The two collections are called People and PeopleFiltered. When i create people I add them to the list called People. When the search is applied it populates the PeopleFiltered list, which is the list the UI is bound to. How can I maintain this list be init to mimic People.
At the end of the day the PeopleFiltered collection should mimic People unless a search is being applied.
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="400" Width="200">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Search:"/>
<TextBox Grid.Column="1" Background="Gold" Text="{Binding SearchString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
<StackPanel Grid.Row="1">
<Expander Header="People" IsExpanded="{Binding IsExpanded, Mode=OneWay}">
<ListView ItemsSource="{Binding PeopleFiltered}">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<Ellipse Width="8" Height="8" Fill="Green" Margin="0,0,5,0"/>
<TextBlock Text="{Binding Name}"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Expander>
</StackPanel>
</Grid>
</Window>
MainWindowViewModel.cs
using System.Collections.ObjectModel;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Linq;
using System.Collections.Generic;
namespace WpfApplication1
{
public class MainWindowViewModel : NotifyBase
{
// search text
private string searchString;
public string SearchString
{
get { return searchString; }
set
{
this.searchString = value;
NotifyPropertyChanged("SearchString");
ApplySearchFilter();
}
}
private void ApplySearchFilter()
{
if (string.IsNullOrWhiteSpace(SearchString))
{
IsExpanded = false;
PeopleFiltered.Clear();
foreach (DisplayItem displayItem in People)
{
PeopleFiltered.Add(displayItem);
}
}
else
{
// open expanders and apply search
IsExpanded = true;
PeopleFiltered.Clear();
foreach (DisplayItem displayItem in People)
{
if (displayItem.Name.ToLowerInvariant().Contains(SearchString.ToLowerInvariant()))
{
PeopleFiltered.Add(displayItem);
}
}
}
}
// used to to open and close expanders
private bool isExpanded;
public bool IsExpanded
{
get { return this.isExpanded; }
set
{
this.isExpanded = value;
NotifyPropertyChanged("IsExpanded");
}
}
// data collections for each expander
private ObservableCollection<DisplayItem> people;
public ObservableCollection<DisplayItem> People
{
get { return people ?? (people = new ObservableCollection<DisplayItem>()); }
set
{
people = value;
NotifyPropertyChanged("People");
}
}
private ObservableCollection<DisplayItem> peopleFiltered;
public ObservableCollection<DisplayItem> PeopleFiltered
{
get { return peopleFiltered ?? (peopleFiltered = new ObservableCollection<DisplayItem>()); }
set
{
peopleFiltered = value;
NotifyPropertyChanged("PeopleFiltered");
}
}
// init
public MainWindowViewModel()
{
// People
People.Add(new DisplayItem() { Name="John" });
People.Add(new DisplayItem() { Name="Veta"});
People.Add(new DisplayItem() { Name="Sammy"});
People.Add(new DisplayItem() { Name = "Sarah" });
People.Add(new DisplayItem() { Name = "Leslie" });
People.Add(new DisplayItem() { Name = "Mike" });
People.Add(new DisplayItem() { Name = "Sherry" });
People.Add(new DisplayItem() { Name = "Brittany" });
People.Add(new DisplayItem() { Name = "Kevin" });
}
}
// class used to display all items
public class DisplayItem
{
public string Name { get; set; }
}
//observable object class
public class NotifyBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Here's a nice approach.
Create a Query property in your View Model, this will be bound to your filter TextBox.
private string _Query;
public string Query
{
get { return _Query; }
set
{
_Query = value;
Filter();
//Notify property changed.
}
}
One thing to note here is the Filter() method. This will be called every time the property changes, I'll get back to this later. Firstly, make sure your TextBox binding is TwoWay, it'll look like this:
<TextBox Text="{Binding Query}" ... />
In your View Model, you will need a collection for each ListBox.
private List<object> _Collection1; //The original collection
private List<object> _FilteredCollection1; //The filtered collection
public List<object> FilteredCollection1
{
get { return _FilteredCollection1; }
set
{
_FilteredCollection1 = value;
//Notify property changed.
}
}
//Some more collections
...
It's important to note here that there is a variable for the original unfiltered collection. This is important because we want to filter this list into a new collection, otherwise we'll just keep filtering over and over and eventually have nothing in the collection.
You'll need to bind the ItemsSource property in your ListBox to the collection(s).
<ListBox ItemsSource="{Binding FilteredCollection1}" ... />
Now, your Filter method can simply filter the _Collection1 variable into the FilteredCollection1 property.
private void Filter()
{
//Perform the filter here for all collections.
FilteredCollection1 = _Collection1.Where(x => x.Something == Query);
//Do the same for all other collections...
}
Note: The above Linq is just an example, I expect yours will be slightly more complicated than that, but you get the idea.
So. Whenever Query gets updated, the Filter collection will fire, update the FilteredCollection properties which will in turn call property changed and update the view.
Alternative Approach
Here's another way.
Instead of using a Filter method, you can instead put your filter code inside the get block in the FilteredCollection properties, like this:
public List<object> FilteredCollection1
{
get
{
return _Collection1.Where(...);
}
}
Then, in your Query property, simply call INotifyPropertyChanged for the collection:
private string _Query;
public string Query
{
get { return _Query; }
set
{
_Query = value;
//Notify property changed.
OnPropertyChanged("FilteredCollection1");
}
}
This will force the view to refresh the FilteredCollection1 property.
Related
I am using AdvanceCollectionView from Windows Community Toolkit as a source for a XAML ListView, to allow sorting and filtering. I am having problems with updating the ListView.
To replicate the issue, I've created a simple Person class. In MainPage XAML I have a ListView MyXAMLList and a Button EditButton. In the MainPage code, I have an ObservableCollection<Person> MyPersonList and AdvancedCollectionView MyPersonACV. In Page_Loaded event I add a person to the list and use AdvancedCollectionView as a source for the list view:
Person p = new Person
{
Name = "John",
Age = 35
};
MyPersonList.Add(p);
MyPersonACV = new AdvancedCollectionView(MyPersonList, true);
MyXAMLList.ItemsSource = MyPersonACV;
This works and I can see John in the list.
In the EditButton code I try to update the item on the list but this isn't working. Both the ObservableCollection and the AdvancedCollectionView are updated, but the XAML list is still displaying the old name "John" instead of "Mary".
MyPersonList[0].Name = "Mary";
Debug.WriteLine(MyPersonList[0].ToString());
Debug.WriteLine(MyPersonACV[0].ToString());
I've tried updating the MyXAMLList.SelectedItem instead, but the same result:
Person p = (Person)MyXAMLList.SelectedItem;
p.Name = "Mary";
I've also tried adding MyPersonACV.Refresh(); but doesn't help.
What am I doing wrong? How can I update an item in the list?
Full code below
Person class:
class Person
{
public string Name {get; set;}
public int Age { get; set; }
public override string ToString()
{
return Name;
}
}
MainPage XAML:
<Page
x:Class="App3.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App3"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Loaded="Page_Loaded">
<Grid>
<StackPanel Orientation="Vertical">
<ListView Height="Auto" Width="Auto" x:Name="MyXAMLList" SelectionMode="Single" IsItemClickEnabled="True"/>
<StackPanel Orientation="Horizontal">
<Button x:Name="EditButton" Content="Edit" Click="EditButton_Click"/>
</StackPanel>
</StackPanel>
</Grid>
</Page>
MainPage cs:
using Microsoft.Toolkit.Uwp.UI;
using System.Collections.ObjectModel;
using System.Diagnostics;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace App3
{
public sealed partial class MainPage : Page
{
private ObservableCollection<Person> MyPersonList = new ObservableCollection<Person>();
private AdvancedCollectionView MyPersonACV;
public MainPage()
{
this.InitializeComponent();
}
private void EditButton_Click(object sender, RoutedEventArgs e)
{
//Change name
MyPersonList[0].Name = "Mary";
//Person p = (Person)MyXAMLList.SelectedItem;
//p.Name = "Mary";
Debug.WriteLine(MyPersonList[0].ToString());
Debug.WriteLine(MyPersonACV[0].ToString());
//MyPersonACV.Refresh();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
//create person
Person p = new Person
{
Name = "John",
Age = 35
};
//add to list
MyPersonList.Add(p);
//set up ListView source
MyPersonACV = new AdvancedCollectionView(MyPersonList, true);
MyXAMLList.ItemsSource = MyPersonACV;
}
}
}
I noticed you override the ToString() method to display each item of ListView. When you update the Name property, even if the value of Name property has updated, since there is no binding relationship between Name property and ListViewItem, and the ToString() method isn't triggered when you update data, the UI isn't updated. It'sbetter to customize the appearance of items using DataTemplate, binding the Name property to the element(e.g. TetxBlock) and implement INotifyPropertyChanged interface. In this case, when the Name proeprty changes, it will provide change notifications to the binding and the UI will update. For exmaple:
.xaml:
<ListView Height="Auto" Width="Auto" x:Name="MyXAMLList" SelectionMode="Single" IsItemClickEnabled="True">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
.cs:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private string name { get; set; }
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged();
}
}
public int Age { get; set; }
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private void EditButton_Click(object sender, RoutedEventArgs e)
{
//Change name
MyPersonList[0].Name = "Mary";
}
I am new to C#/WPF. There is a view with one button defined, when the view is initialized, buttons will display a set of reason codes got from DataContext (viewmodel), once any button is clicked, the code on it will be saved and passed forward for next processing.
Q: The text on buttons are totally empty, but the clicked code can be captured, so where the problem is about binding? Thanks.
XAML:
<Button x:Name="btnReason" Command="{Binding DataContext.SelectCommand, RelativeSource={RelativeSource AncestorType=v:View, Mode=FindAncestor}}" CommandParameter="{Binding}" Width="190" Height="190" >
<Border Background="Transparent">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock x:Name="Reason" Grid.Row="0" Text="{Binding ?????}" TextWrapping="Wrap" />
</Grid>
</Border>
</Button>
The code on C#:
public class ReasonsViewModel : ViewModel
{
private IEnumerable<string> m_Names;
public IEnumerable<string> Names
{
get { return m_Names; }
set
{
if (m_Names != value)
{
m_Names = value;
OnPropertyChanged(() => Names);
}
}
}
private string m_SelectedName;
public string SelectedName
{
get { return m_SelectedName; }
set
{
if (m_SelectedName != value)
{
m_SelectedName = value;
OnPropertyChanged(() => SelectedName);
}
}
}
public DelegateCommand SelectCommand { get; private set; }
public ReasonsViewModel()
{
SelectCommand = new DelegateCommand(p => SelectCommandExecute(p));
}
private bool m_Processing;
private void SelectCommandExecute(object item)
{
if (m_Processing) return;
try
{
m_Processing = true;
var name = item as string;
if (name == null) return;
SelectedName = name;
}
finally
{
m_Processing = false;
}
}
}
If I understood your question correctly than your property text in your TextBlock should be bound to SelectedName.
The problem is that your CommandParameter is bound to DataContext. That's what an empty {Binding} statement bounds to. This means your command handler always returns after the null check.
I also suggest that you change your Names proeprty from IEnumerable<string> to ObservableCollection<string>.
ObservableCollection raises events on any additions or removalof items inside and WPF components can bind to these events.
So many examples found and none fit! My list box is a list of Result objects. Results can be checked or unchecked in a listbox to mark them as 'Allowed to 'transmit.
<ListBox
x:Name="FileListBox"
ItemsSource="{Binding TestResults}"
ItemTemplate="{StaticResource FileListTemplate}"
SelectionMode="Single"
SelectedItem="{Binding FileListSelected}"
Background="#FFFFFBE2" />
The FileListTemplate
<DataTemplate x:Key="FileListTemplate">
<Grid HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width=".3*" />
<ColumnDefinition Width=".2*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding FileName}" />
<TextBlock Grid.Column="1"
Text="Machine">
</TextBlock>
<CheckBox x:Name="UploadOK"
Grid.Column="2"
HorizontalAlignment="Right"
IsChecked="{Binding CanUpload, Mode=TwoWay}" />
</Grid>
</DataTemplate>
I took out a lot of formatting code to reduce the clutter. So when the check box is checked (or un checked) I need to set a boolean on the object to true or false. But I do not want the ListItem selected just because the checkbox is selected. When the ListItem is selected something else happens. Here is the code for that.
public TestResult FileListSelected
{
get
{
return selectedItem;
}
set
{
if (value == selectedItem)
return;
selectedItem = value;
if (!Workspaces.Any(p => p.DisplayName == value.FileName))
{
this.DisplayTestResult(value as TestResult);
}
base.RaisePropertyChanged("FileListSelected");
}
}
And here is the code I bound to for the Checkbox (although it didn't work).
public bool CanUpload
{
get { return selectedItem.CanUpload; }
set
{
selectedItem.CanUpload = value;
}
}
I appreciate you looking at this.
Internal Class TestResult
{
...
private bool _canUpload;
public bool CanUpload
{
get { return _canUpload; }
set
{
_canUpload = value;
base.RaisePropertyChanged("CanUpload");
}
}
}
When working with MVVM always check for the following:
Add using System.ComponentModel; to your ViewModelClass
Inherit from INotifyPropertyChanged
Always check your DataContext and see the Output Window for BindingErrors
Create Bindings like this:
Example Property:
public string Example
{
get { return _example; }
set
{
_example= value;
OnPropertyChanged();
}
}
this will call OnPropertyChanged automatically every time a new value is assigned (not updated automaticaly once it changes from some other location!)
Make sure your Implementation of INotifyPropertyChanged looks like this:
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
for that you also need using System.Runtime.CompilerServices;
Other options to get your code working:
Your TestResults sould be an ObservableCollection<TestResult>
TestResult should have a property for CanUpload and FileName and inherit from INotifyPropertyChanged
Then on your MainViewModel for example on and ButtonClick your can get the selected files like this:
private List<string> GetSelectedFiles()
{
return TestResults.Where(result => result.CanUpload == true).Select(r => r.FileName).ToList());
}
Note:
FileListSelected is a Property of your ListBox's DataContext which is different to the DataContext of an entry (or at least should be).
FileListSelected will then return the selected Item of your ItemsSource.
Maybe you can comment on this problem with the row selection/checkbox check and add some detail so I can help you more.
EDIT: Notify MainWindowViewModel about CheckBox State Changes:
I see two possible approaches here:
USING EVENT
Add this to your TestResult class:
public delegate void CheckBoxStateChangedHandler(object sender, CheckBoxStateChangedEventArgs e);
public event CheckBoxStateChangedHandler CheckBoxStateChanged;
public class CheckBoxStateChangedEventArgs
{
bool CheckBoxChecked { get; set; }
}
Make sure that on creation of a new TestResult in your MainViewModel you subscribe to that event;
testResult.CheckBoxStateChanged += CheckBox_StateChanged;
Handle what you want to do once the state is changed in CheckBox_StateChanged. Note that the argument e contains the boolean (Checked) and the corresponding TestResult as the sender.
You simply invoke your new Event in the Setter of your CheckBox.Checked Binding:
public bool Checked
{
get { return _checked; }
set
{
_checked = value;
OnPropertyChanged();
CheckBoxStateChanged.Invoke(this, new CheckBoxStateChangedEventArgs() { CheckBoxChecked = value })
}
}
CALL METHOD ON MAINWINDOWVIEWMODEL
for that you need o create a static object of your MainWindowViewModel (in your MainViewModel) - don't forget to assigne a value once you create your MainWindowViewModel.
public static MainViewModel Instance { get; set; }
then simply add a public Method as you need:
public void CheckBoxValueChanged(bool value, TestResult result)
{
//Do whatever
}
you can also call in from the same spot as the event from above is invoked.
public bool Checked
{
get { return _checked; }
set
{
_checked = value;
OnPropertyChanged();
MainWindowViewModel.Instance.CheckBoxValueChanged(value, this);
}
}
I'm using Caliburn.Micro for WPF (using VS 2012 and targeting to .NET 4.5.1).
I have problem with binding itemsSource to ComboBox (but I investigate that in my case it happens also with other controls with ItemsSource property, like ListBox).
I have nested views (usercontrols) with viewmodels created with SimpleContainer (IoC).
Here is my problem:
Combobox is populated with items not from its view viewmodel (LanguageSelectionViewModel) but from parent view viewmodel (TopViewModel).
Also, when I removed items collection from parent viewmodel, my combobox was empty.
Code:
MainWindowView.xaml:
<Window
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300"
d:DataContext="{d:DesignInstance d:Type=mainWindow:MainWindowViewModel}"
>
<Grid>
<top:TopView
HorizontalAlignment="Stretch"
cal:Bind.Model="{Binding TopVM}"
/>
</Grid>
</Window>
MainWindowViewModel:
public class MainWindowViewModel : Screen
{
private TopViewModel topVm;
public TopViewModel TopVM
{
get { return topVm; }
set
{
topVm = value;
NotifyOfPropertyChange(() => TopVM);
}
}
public MainWindowViewModel(TopViewModel topVm, ContentViewModel contentVm)
{
TopVM = topVm;
TopVM.ConductWith(this);
}
}
TopView.xaml:
<UserControl>
<StackPanel Orientation="Horizontal">
<languageSelection:LanguageSelectionView cal:Bind.Model="{Binding LanguageSelectionVM}"/>
</StackPanel>
</UserControl>
TopViewModel.cs:
public class TopViewModel : Screen
{
private LanguageSelectionViewModel _languageSelectionVM;
public LanguageSelectionViewModel LanguageSelectionVM
{
get { return _languageSelectionVM; }
set
{
_languageSelectionVM = value;
NotifyOfPropertyChange(() => LanguageSelectionVM);
}
}
public TopViewModel(ClockViewModel clockVm, LanguageSelectionViewModel languageSelectionVM)
{
this.Items = new ObservableCollection<string>() { "a", "a", "a" };
LanguageSelectionVM = languageSelectionVM;
LanguageSelectionVM.ConductWith(this);
}
private ObservableCollection<string> _items;
public ObservableCollection<string> Items
{
get { return _items; }
set
{
_items = value;
NotifyOfPropertyChange(() => Items);
}
}
}
LanguageSelectionView.xaml:
<UserControl>
<StackPanel Orientation="Vertical">
<ComboBox ItemsSource="{Binding Items}"/>
</StackPanel>
</UserControl>
LanguageSelectionViewModel.cs:
public class LanguageSelectionViewModel : Screen
{
private ObservableCollection<string> _items;
public ObservableCollection<string> Items
{
get { return _items; }
set
{
_items = value;
NotifyOfPropertyChange(() => Items);
}
}
public LanguageSelectionViewModel()
{
this.Items = new ObservableCollection<string>() { "1", "a" };
}
}
I had also tried to populate this collection later, with no success:
protected override void OnViewReady(object view)
{
base.OnViewReady(view);
this.Items = new ObservableCollection<string>() { "1", "a" };
Refresh();
}
DataContext seems to be okay, because binding to textbox
<TextBlock Text="{Binding TestString}"/>
works fine.
Ok, mystery solved.
Instead of nesting controls like this:
<Grid>
<top:TopView
cal:Bind.Model="{Binding TopVM}" />
</Grid>
I should write:
<Grid>
<ContentControl
cal:View.Model="{Binding TopVM}" />
</Grid>
And there is no need to force DataContext.
I figure out that ComboBox whas the only control that had DataContext set to parent View Model, not to proper View model.
It works by forcing it in this way:
<ComboBox
DataContext="{Binding}"
ItemsSource="{Binding Items}" >
But still is the question - why? This is bug or feature of Caliburn.Micro?
I need to bind a ComboBox's Selected Item according to a value of a field in the data context which it's parent container receives .
the container is a grid which receives it's datacontext from an item in an itemcontrol when it's clicked
private void Button_Click(object sender , RoutedEventArgs e)
{
GridEmployee.DataContext = ((Button)sender).DataContext;
}
*the Button got it's itemsource from a list of Employee's Bounded to the itemControl
the grid holds some controls amongst them a combobox which i initalize through an Enum
public Enum Gender
{
Male,Female
};
foreach(string _gender in Enum.GetNames(Gender) )
{
GenderComboBox.Items.Add(_gender);
}
the Employee class has a matching Property Gender
private string gender;
public string Gender
{
get{return gender;}
set
{
gender = value ;
if( PropertyChanged != null )
PropertyChanged(this,new PropertyChangedEventArgs("Gender"));
}
}
the GenderComboBox.SelectedItem is bounded to the value of the Gender Property for the bounded object Employee
<ComboBox x:Name="GenderComboBox" SelectedItem="{Binding Gender , Mode=TwoWay}" />
the problem here of course that the item does not get selected ..
I tought may be its becuase the items in the combobox are strings and I try to bound them according to a custom converter which just take the Enum Value and returns the .ToString()
of it .
but I was not able to check this becuase that threw an An XamlParseException in form's contractor .
which I did not fully understand why it happend ,may be becuase it did not have a value to convert when I form loads.
so to conclude how do I bind a Property from My Employee Class
to a combobox with the string representation of the Property's Value?
Works nicely in my case....
XAML
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tk="http://schemas.microsoft.com/wpf/2008/toolkit"
Title="GenderSelection" Height="100" Width="300" x:Name="MyWindow">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock FontSize="40"
Text="{Binding MyGender, ElementName=MyWindow, Mode=TwoWay}"/>
<ComboBox Grid.Row="1"
ItemsSource="{Binding Genders, ElementName=MyWindow}"
SelectedItem="{Binding MyGender, ElementName=MyWindow, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Window>
Code Behind
public enum Gender
{
Male,
Female
}
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window, INotifyPropertyChanged
{
private string myGender = Gender.Male.ToString();
public string MyGender
{
get
{
return myGender;
}
set
{
myGender = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("MyGender"));
}
}
}
public string[] Genders
{
get
{
return Enum.GetNames(typeof(Gender));
}
}
public Window1()
{
InitializeComponent();
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
Let me know if this guides you in correct direction...
Just change the initialization of your ComboBox to
foreach(string _gender in Enum.GetNames(Gender) )
{
GenderComboBox.Items.Add(_gender.ToString());
}
That should work, because your Gender property of the Employees class returns a string.