WPF simple binding problem - wpf

Trying to understand this binding process of the WPF.
See the code at the bottom.
In my "viewmodel", see the code at the bottom, i have an observable collection that is populating the listview with the items. Thats the one that contains a path called symbol to set the selected index in the combobox. Now my problem is that i need to populate the combobox from another list before its added to the listview (some default values).
Since i just started with WPF i thought that perhaps you can use 2 different ObservableCollections in the same class to achieve this but that didn't work. So how can i populate the datatemplate with the default values before they are bound/added to the listview?
This is what i use to populate the listview, see the viewmodelcontacts at the bottom.
I also tried to add another class with a new observablecollection that i could use in my combobox, but i didn't get that to work either.
The data that should be populated comes from a XML file located as a resource in my app.
Another question, is it possible to add commands to images? or are commands only available from controls that inherit from the button_base class? I wanted to use an image next to an element and when the user clicked on that image they would remove the element.
From the answer below, is it possible without adding a button since i don't want the button feeling (for instance when hovering and clicking)*
Window.xaml:
<r:RibbonWindow x:Class="Onyxia_KD.Windows.ContactWorkspace"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
Title="Contact card" ResizeMode="NoResize" Height="600" Width="600"
Background="White">
<r:RibbonWindow.Resources>
<DataTemplate x:Key="cardDetailFieldTemplate">
<TextBox Text="{Binding Path=Field}" MinWidth="150"></TextBox>
</DataTemplate>
<DataTemplate x:Key="cardDetailValueTemplate">
<TextBox Text="{Binding Path=Value}" MinWidth="150"></TextBox>
</DataTemplate>
<DataTemplate x:Key="cardDetailSymbolTemplate">
<!-- Here is the problem. Populating this with some default values for each entry before the selectedIndex is bound from the datasource -->
<ComboBox SelectedIndex="{Binding Path=Symbol}" DataContext="_vmSettings" ItemsSource="{Binding Symbols}" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalAlignment="Center">
<ListViewItem Padding="0,3,0,3" Content="{Binding Path=Value}" />
</ComboBox>
</DataTemplate>
<DataTemplate x:Key="cardDetailCategoryTemplate">
<ComboBox SelectedIndex="{Binding Path=Category}" Grid.Column="1" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalAlignment="Center">
<!--same as the combobox above but categories instead of symbols-->
</ComboBox>
</DataTemplate>
</r:RibbonWindow.Resources>
...
<ListView ItemsSource="{Binding ContactData}" Foreground="Black" SelectionMode="Single" x:Name="cardDetailList" KeyDown="cardDetailList_KeyDown">
<ListView.View>
<GridView>
<GridViewColumn Header="Category" Width="auto" CellTemplate="{StaticResource cardDetailCategoryTemplate}"></GridViewColumn>
<GridViewColumn Header="Field" Width="auto" CellTemplate="{StaticResource cardDetailFieldTemplate}"></GridViewColumn>
<GridViewColumn Header="Symbol" Width="70" CellTemplate="{StaticResource cardDetailSymbolTemplate}"></GridViewColumn>
<GridViewColumn Header="Value" Width="auto" CellTemplate="{StaticResource cardDetailValueTemplate}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Code behind:
private ViewModelContacts _vm;
public ContactWorkspace()
{
InitializeComponent();
_vm = new ViewModelContacts();
this.DataContext = _vm;
}
private void Run_MouseUp(object sender, MouseButtonEventArgs e)
{
_vm.AddNewDetail();
}
private void Image_MouseUp(object sender, MouseButtonEventArgs e)
{
_vm.AddNewDetail();
}
private void cardDetailList_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Delete)
{
if (MessageBox.Show("Are you sure that you want to delete this?", "Warning!", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
{
_vm.RemoveDetail(cardDetailList.SelectedIndex);
}
}
}
ViewModelContacts:
public ObservableCollection<ContactCardData> ContactData { get; set; }
public ViewModelContacts()
{
ContactData = new ObservableCollection<ContactCardData>();
Populate();
}
private void Populate()
{
ContactData.Add(new ContactCardData("Test", 0, 0, "Value123"));
ContactData.Add(new ContactCardData("Test2", 1, 1, "Value1234"));
ContactData.Add(new ContactCardData("Test3", 2, 2, "Value1235"));
ContactData.Add(new ContactCardData("Test4", 3, 3, "Value12356"));
}
public void UpdateNode()
{
ContactData.ElementAt(0).Value = "Giraff";
}
public void AddNewDetail()
{
ContactData.Add(new ContactCardData());
}
public void RemoveDetail(int position)
{
ContactData.RemoveAt(position);
}
ViewModelContactData:
public class ContactCardData : DependencyObject
{
public int Category
{
get { return (int)GetValue(CategoryProperty); }
set { SetValue(CategoryProperty, value); }
}
// Using a DependencyProperty as the backing store for Category. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CategoryProperty =
DependencyProperty.Register("Category", typeof(int), typeof(ContactCardData), new UIPropertyMetadata(0));
public string Field
{
get { return (string)GetValue(FieldProperty); }
set { SetValue(FieldProperty, value); }
}
// Using a DependencyProperty as the backing store for Field. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FieldProperty =
DependencyProperty.Register("Field", typeof(string), typeof(ContactCardData), new UIPropertyMetadata(""));
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(ContactCardData), new UIPropertyMetadata(""));
public int Symbol
{
get { return (int)GetValue(SymbolProperty); }
set { SetValue(SymbolProperty, value); }
}
// Using a DependencyProperty as the backing store for Symbol. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SymbolProperty =
DependencyProperty.Register("Symbol", typeof(int), typeof(ContactCardData), new UIPropertyMetadata(0));
public ContactCardData()
{
}
public ContactCardData(string field, int category, int symbol, string value)
{
this.Symbol = symbol;
this.Category = category;
this.Field = field;
this.Value = value;
}
}

Define your Window class as follows (explanation will be later):
<Window x:Class="TestCustomTab.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:TestCustomTab="clr-namespace:TestCustomTab" Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="workingTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}"/>
<ComboBox Grid.Column="1" SelectedIndex="0" ItemsSource="{Binding Symbols}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Ellipse Grid.Column="0" Fill="Red" Height="5" Width="5"/>
<TextBlock Grid.Column="1" Text="{Binding}" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</DataTemplate>
<DataTemplate x:Key="personalTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}"/>
<ComboBox Grid.Column="1" SelectedIndex="0" ItemsSource="{Binding Symbols}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Ellipse Grid.Column="0" Fill="Green" Height="5" Width="5"/>
<TextBlock Grid.Column="1" Text="{Binding}" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</DataTemplate>
<TestCustomTab:ContactDataByTypeTemplateSelector x:Key="contactDataByTypeTemplateSelector"/>
</Window.Resources>
<Grid x:Name="grid">
<ListView ItemsSource="{Binding ContactDataCollection, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TestCustomTab:Window1}}}">
<ListView.View>
<GridView>
<GridViewColumn Header="Column" Width="200" CellTemplateSelector="{StaticResource contactDataByTypeTemplateSelector}">
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Lets assume that your ContactData looks as follows:
public class ContactData : INotifyPropertyChanged
{
private string _name;
private int _homePhone;
private int _mobilePhone;
private string _value;
private ContactDataType _dataType;
public ContactData()
{
}
public ContactData(string name, int homePhone, int mobilePhone, string value, ContactDataType dataType)
{
_name = name;
_dataType = dataType;
_value = value;
_mobilePhone = mobilePhone;
_homePhone = homePhone;
}
#region Implementation of INotifyPropertyChanged
public ContactDataType DataType
{
get { return _dataType; }
set
{
if (_dataType == value) return;
_dataType = value;
raiseOnPropertyChanged("DataType");
}
}
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
raiseOnPropertyChanged("Name");
}
}
public int HomePhone
{
get { return _homePhone; }
set
{
if (_homePhone == value) return;
_homePhone = value;
raiseOnPropertyChanged("HomePhone");
}
}
public int MobilePhone
{
get { return _mobilePhone; }
set
{
if (_mobilePhone == value) return;
_mobilePhone = value;
raiseOnPropertyChanged("MobilePhone");
}
}
public string Value
{
get { return _value; }
set
{
if (_value == value) return;
_value = value;
raiseOnPropertyChanged("Value");
raiseOnPropertyChanged("Symbols");
}
}
public ReadOnlyCollection<char> Symbols
{
get
{
return !string.IsNullOrEmpty(_value) ? new ReadOnlyCollection<char>(_value.ToCharArray()) : new ReadOnlyCollection<char>(new List<char>());
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void raiseOnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
It has DataType property of type ContactDataType which is enum:
public enum ContactDataType
{
Working,
Personal
}
In ability to have different DataTemplates for the same entities differentiated by some feature you need to use DataTemplateSelector. The technique is in inheriting from DataTemplateSelector and overriding SelectTemplate method. In our case:
public class ContactDataByTypeTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var contactData = item as ContactData;
var control = container as FrameworkElement;
if (contactData != null & control != null)
switch (contactData.DataType)
{
case ContactDataType.Working:
return control.TryFindResource("workingTemplate") as DataTemplate;
case ContactDataType.Personal:
return control.TryFindResource("personalTemplate") as DataTemplate;
default:
return base.SelectTemplate(item, container);
}
return base.SelectTemplate(item, container);
}
}
Here is Window1 class in code behind:
public partial class Window1 : Window
{
private ObservableCollection<ContactData> _contactDataCollection = new ObservableCollection<ContactData>();
public Window1()
{
ContactDataCollection.Add(new ContactData("test1", 0, 1, "value1", ContactDataType.Working));
ContactDataCollection.Add(new ContactData("test2", 0, 1, "value2", ContactDataType.Working));
ContactDataCollection.Add(new ContactData("test3", 0, 1, "value3", ContactDataType.Working));
ContactDataCollection.Add(new ContactData("test4", 0, 1, "value4", ContactDataType.Personal));
ContactDataCollection.Add(new ContactData("test5", 0, 1, "value5", ContactDataType.Personal));
InitializeComponent();
}
public ObservableCollection<ContactData> ContactDataCollection
{
get { return _contactDataCollection; }
}
}
Now explanation:
We created some class that we need to represent to user (ContactData) and let him to have feature - ContactDataType.
We created 2 DataTemplates in resources (x:Key is important) for ContactDataType.Working and ContactDataType.Personal
We created DataTemplateSelector to have ability switch templates by feature.
In our first GridViewColumn we defined CellTemplateSelector and bind to it our ContactDataByTypeTemplateSelector.
In runtime whenever the collection changes ContactDataByTypeTemplateSelector select to us template based on item feature and we may have any number of templates for any number of defined features.
Notice: change TestCustomTab for your namespace.

For the last question, you can use a button and template it to include an image.

Related

WPF ListView - column header not as property, but as Key of Dictionary (Dictionary is ListViewItem property) - ComparisionMatrixControl

I would like to make a MatrixControl in my WPF app. First thing it starts from ListView and defines 'MatrixLine' in the model. ListView ItemsSource was an ObservableCollection. I have a simple complete model that shows what I'm aiming for and a picture with the result I expect for the created model.
I have a problem with how to add the 'MatrixLine' property type of Dictionary, [Keys] as a columns header in the ListView and [Values] (boolean) as a sign 'x' on te ListView. (The picture with the result that follows)
Expected result for my model
Result for my model creating in ViewModel constructor
GitHub Project
https://github.com/Varran/WPF_Multiporownywarki_Baza
Model
public class ColorBase
{
public string Name { get; }
public int Saturation { get; private set; }
public ColorBase(string name, int saturation)
{
this.Name = name;
this.Saturation = saturation;
}
public void ChangeSaturation(int newSaturation)
{
Saturation = newSaturation;
}
public override string ToString()
{
return $"ColorBase: {Saturation.ToString().PadLeft(4, ' ')} - '{Name}'";
}
}
public class MixedPaint
{
public string PaintName { get; }
public List<ColorBase> Ingredients { get; }
public MixedPaint(string name)
{
Ingredients = new List<ColorBase>();
this.PaintName= name;
}
public MixedPaint AddIngredient(ColorBase color)
{
bool added = false;
foreach (var item in Ingredients)
{
if (item.Name == color.Name )
{
item.ChangeSaturation(item.Saturation + color.Saturation);
added = true;
}
}
if (!added)
Ingredients.Add(color);
return this;
}
}
public class MatrixLine
{
public ColorBase ColorIngredient { get; private set; }
public Dictionary<string, bool> Matrix;
public MatrixLine(ColorBase color)
{
Matrix = new Dictionary<string, bool>();
this.ColorIngredient = color;
}
public void AddToMatrix(MixedPaint mixedPaint)
{
string paintName = mixedPaint.PaintName;
bool doesItContainIgredient = mixedPaint.Ingredients.Any(o => (o.Name == ColorIngredient.Name &&
o.Saturation == ColorIngredient.Saturation));
Matrix.Add(paintName, doesItContainIgredient);
}
}
ViewModel
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<MixedPaint> mixedPaints;
public ObservableCollection<MixedPaint> MixedPaints { get { return mixedPaints; } }
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private MixedPaint selectedMixedPaint;
public MixedPaint SelectedMixedPaint {
get { return selectedMixedPaint; }
set { selectedMixedPaint = value;
OnPropertyChanged(nameof(SelectedMixedPaint)); } }
private ObservableCollection<MatrixLine> comparisonMatrix;
public ObservableCollection<MatrixLine> ComparisonMatrix { get { return comparisonMatrix; } }
public ViewModel()
{
ColorBase yellowA = new ColorBase("YellowA", 110);
ColorBase yellowB = new ColorBase("YellowB", 175);
ColorBase blueA = new ColorBase("BlueA", 77);
ColorBase blueB = new ColorBase("BlueB", 135);
ColorBase redA = new ColorBase("RedA", 95);
ColorBase redB = new ColorBase("RedB", 225);
ColorBase whiteA = new ColorBase("WhiteA", 200);
MixedPaint greenA = new MixedPaint("GreenLight")
.AddIngredient(yellowA)
.AddIngredient(blueA);
MixedPaint greenB = new MixedPaint("GreenDark")
.AddIngredient(yellowB)
.AddIngredient(blueB);
MixedPaint orangeA = new MixedPaint("OrangeLight")
.AddIngredient(yellowA)
.AddIngredient(redB)
.AddIngredient(whiteA);
MixedPaint orangeB = new MixedPaint("OrangeDark")
.AddIngredient(yellowB)
.AddIngredient(redB);
MixedPaint violet = new MixedPaint("Violet")
.AddIngredient(redA)
.AddIngredient(blueB);
mixedPaints = new ObservableCollection<MixedPaint>() { greenA, greenB, orangeA, orangeB, violet };
SelectedMixedPaint = greenA;
List<ColorBase> uniqueColorsBase = new List<ColorBase>();
foreach (var item in mixedPaints)
foreach (var item2 in item.Ingredients)
if (!uniqueColorsBase.Contains(item2))
uniqueColorsBase.Add(item2);
uniqueColorsBase = uniqueColorsBase.OrderBy(o => o.Name).ThenBy(o => o.Saturation).ToList();
comparisonMatrix = new ObservableCollection<MatrixLine>();
foreach (var color in uniqueColorsBase)
{
MatrixLine line = new MatrixLine(color);
foreach (var mixed in mixedPaints)
line.AddToMatrix(mixed);
comparisonMatrix.Add(line);
}
}
}
View
<Window x:Class="WPF_multi_próby.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:local="clr-namespace:WPF_multi_próby"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="150"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Name="ListOfMixedPaint"
Grid.Row="0" Grid.Column="0" Orientation="Vertical">
<TextBlock Text="List of MixedPaint:"/>
<ListView ItemsSource="{Binding MixedPaints}" SelectedItem="{Binding SelectedMixedPaint}" Margin="10">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="PaintName: "/>
<TextBlock Text="{Binding PaintName}" Width="120" FontWeight="Bold"/>
<TextBlock Text="IngradientCount: " Margin="0,0,10,0"/>
<TextBlock Text="{Binding Ingredients.Count}" FontWeight="Bold"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<StackPanel Name="BOM"
Grid.Column="1" Grid.Row="0">
<TextBlock Text="Ingredients of selected MixedPaint"/>
<ListView ItemsSource="{Binding SelectedMixedPaint.Ingredients}" Margin="10">
<ListView.View>
<GridView>
<GridViewColumn Header="Color Name" DisplayMemberBinding="{Binding Name}" Width="100"/>
<GridViewColumn Header="Color Saturation" DisplayMemberBinding="{Binding Saturation}" Width="100"/>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
<StackPanel Name="MultiComparerOfPaints"
Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Orientation="Vertical">
<TextBlock Text="Multicomparer of paints"/>
<ListView ItemsSource="{Binding ComparisonMatrix}" Margin="10" FontFamily="Cascadia Code" >
<ListView.View>
<GridView>
<GridViewColumn Header="Unique ingredient" DisplayMemberBinding="{Binding ColorIngredient}" Width="180"/>
<!-- no idea how binding -->
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Grid>
</Window>
I think this is my solution.
I do not fully understand this solution yet, but I will have to try.
DataMatrix in WPF - codeproject
or
Binding matrix arrays to WPF DataGrid

WPF Changing Datacontexts and views in same window

I am new to WPF am and porting an application from VC++ 6.0/MFC to c#/WPF (VS2013). Most of my windows development has been in VC++/MFC. I am trying to stick to the MVVM pattern and am writing a few proof of concept apps to get my feet wet. I am having one sticking point so far.
When my app starts up it will present a tree view of customers and bills. I have that working well using a simple hierarchical data template with each level binding to my local data type (view model). What I want to have happen is when a bill is selected (right now I have a button to press on the bill template) I want the treeview to be replaced by a detail view of the bill (I don't want a dialog to pop up).
The Xaml for this is:
<DockPanel>
<TreeView x:Name="trvGroups" ItemsSource="{Binding LBGroups}" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling">
<TreeView.ItemContainerStyle>
<!--
This Style binds a TreeViewItem to a LBtreeViewItemViewModel
-->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:GroupViewModel}"
ItemsSource="{Binding Children}"
>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding GroupName}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:BillViewModel}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding BillName}" />
<Button Command="{Binding Path=BillEditCommand}">Edit</Button>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</DockPanel>
Right now I have more questions than anything. Should I define each view as user controls and put them in window.resources? Do I use data templates? I assume I would change the data context to point to the detail bill view model. What is the best way to do this?
My goal, to adhere to MVVM as I understand it, is to have nothing in the code behind (or as little as possible).
I'm looking more for pointers to get me started along the right path as I research. I getting a little befuddled at the moment.
Thanks in advance.
I'll Show you a plain Master Details Scenario where you can choose models in your TreeView and Edit Them.
CS :
public partial class MainWindow : Window , INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private ICommand onEditBillCommand;
public ICommand OnEditBillCommand
{
get
{
if (onEditBillCommand == null)
onEditBillCommand = new RelayCommand<Bill>
(
bill => { CurrentBill = bill; }
);
return onEditBillCommand;
}
}
private Bill currectBill;
public Bill CurrentBill
{
get { return currectBill; }
set
{
currectBill = value;
PropertyChanged(this, new PropertyChangedEventArgs("CurrentBill"));
}
}
public List<Customer> Customers
{
get
{
List<Customer> customers = new List<Customer>();
for (int i = 0; i < 5; i++)
{
customers.Add(CreateMockCustomer(i));
}
return customers;
}
}
private Customer CreateMockCustomer(int g )
{
Customer c = new Customer();
c.Name = "John (" + g + ")" ;
for (int i = 0; i < 3; i++)
{
c.Bills.Add(CreateMockBill());
}
return c;
}
private Bill CreateMockBill()
{
Bill b = new Bill();
b.Price = 55.5;
b.BoughtOnDate = DateTime.Now.Date;
return b;
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public class Customer : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private ObservableCollection<Bill> bills;
public ObservableCollection<Bill> Bills
{
get
{
if (bills == null)
{
bills = new ObservableCollection<Bill>();
}
return bills;
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public class Bill : INotifyPropertyChanged
{
private double price;
public double Price
{
get { return price; }
set
{
price = value;
PropertyChanged(this, new PropertyChangedEventArgs("Price"));
}
}
private DateTime boughtOnDate;
public DateTime BoughtOnDate
{
get { return boughtOnDate; }
set
{
boughtOnDate = value;
PropertyChanged(this, new PropertyChangedEventArgs("BoughtOnDate"));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public interface IRelayCommand : ICommand
{
void RaiseCanExecuteChanged();
}
public class RelayCommand<T> : IRelayCommand
{
private Predicate<T> _canExecute;
private Action<T> _execute;
public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
private void Execute(T parameter)
{
_execute(parameter);
}
private bool CanExecute(T parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public bool CanExecute(object parameter)
{
return parameter == null ? false : CanExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
var temp = Volatile.Read(ref CanExecuteChanged);
if (temp != null)
temp(this, new EventArgs());
}
}
XAML :
<Window>
<Window.Resources>
<HierarchicalDataTemplate x:Key="customerTemplate" DataType="{x:Type local:Customer}" ItemsSource="{Binding Bills}">
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Price}" />
<TextBlock Text="{Binding BoughtOnDate}" Grid.Column="1" />
<Button Content="Edit" Grid.Column="2"
Command="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=DataContext.OnEditBillCommand}"
CommandParameter="{Binding}"/>
</Grid>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<TextBlock Text="{Binding Name}" FontFamily="Arial" FontSize="16" FontWeight="Bold" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="0.05*"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TreeView ItemsSource="{Binding Customers}" ItemTemplate="{StaticResource customerTemplate}">
</TreeView>
<Grid Grid.Column="2" DataContext="{Binding CurrentBill, Mode=OneWay}" Background="AliceBlue">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Text="{Binding Price, Mode=TwoWay}" Margin="50"/>
<TextBox Text="{Binding BoughtOnDate, Mode=TwoWay}" Grid.Row="1" Margin="50"/>
</Grid>
</Grid>

WPF MVVM and Observablecollect

I have a wpf application and I want to update my listview when I change the value through the UI using Observablecollect. But I don't get what I expect. When I change the value I won't update my list view.
View Code(Xaml)
<UserControl x:Class="DataWatch.View.CompareData"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ViewModels="clr-namespace:DataWatch.ViewModel"
mc:Ignorable="d"
d:DesignHeight="340" d:DesignWidth="600">
<UserControl.DataContext>
<ViewModels:CompareViewModel/>
</UserControl.DataContext>
<Grid>
<ListView HorizontalAlignment="Left" Name="comparelistview" VerticalAlignment="Top" Width="600" Height="340" ItemsSource="{Binding DisplayData}">
<ListView.View>
<GridView>
<GridViewColumn Width="150" Header="Key"
DisplayMemberBinding="{Binding Path=Key}" />
<GridViewColumn Width="150" Header="Project Data"
DisplayMemberBinding="{Binding Path=ProjectData}" />
<GridViewColumn Width="150" Header="Import Data"
DisplayMemberBinding="{Binding Path=ImportData}"/>
<GridViewColumn Width="150" Header="State"
DisplayMemberBinding="{Binding Path=State}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
ViewModel
namespace DataWatch.ViewModel
{
public class CompareViewModel
{
private ObservableCollection<CompareDiplayData> _displayData;
public CompareViewModel()
{
_displayData = new ObservableCollection<CompareDiplayData>();
}
public ObservableCollection<CompareDiplayData> DisplayData
{
get { return _displayData; }
}
}
Model:
namespace DataWatch.Model
{
public class CompareDiplayData : INotifyPropertyChanged
{
private string _key;
public string Key
{
set
{
_key = value;
this.Changed("Key");
}
get
{
return _key;
}
}
private string _projectData;
public string ProjectData
{
set
{
_projectData = value;
this.Changed("ProjectData");
}
get
{
return _projectData;
}
}
private string _importData;
public string ImportData
{
set
{
_importData = value;
this.Changed("ImportData");
}
get
{
return _importData;
}
}
private string _state;
public string State
{
set
{
_state = value;
this.Changed("State");
}
get
{
return _state;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void Changed(string PropertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
When I change the value in displayData ,but Listview won't update the data.
combobox view control
<UserControl x:Class="DataWatch.View.SelectPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ViewModels="clr-namespace:DataWatch.ViewModel"
xmlns:AttachProperty="clr-namespace:DataWatch.AttachedProperty"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="600">
<UserControl.DataContext>
<ViewModels:CompareViewModel/>
</UserControl.DataContext>
<Grid Name="good">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150">
</ColumnDefinition>
<ColumnDefinition Width="300">
</ColumnDefinition>
<ColumnDefinition Width="150">
</ColumnDefinition>
</Grid.ColumnDefinitions>
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Column="0" Grid.Row="0" Margin="15,4,6,4" x:Name="KeyComboBox" VerticalAlignment="Top" Width="120" Text="Choose Key" AttachProperty:SelectionBehavior.SelectionChanged="{Binding SelectKeyCmd}" SelectedItem="{Binding SelectedKey, Mode=TwoWay}" ItemsSource="{Binding KeyComboboxItem}"
IsEditable="true" IsReadOnly="true"
IsDropDownOpen="True" StaysOpenOnEdit="True"/>
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Column="1" Grid.Row="0" Margin="90,4,6,4" Name="compareitemcomBox" VerticalAlignment="Top" Width="120" Text="Compare Item" AttachProperty:SelectionBehavior.SelectionChanged="{Binding SelectKeyCmd}" SelectedItem="{Binding SelectedComparedData, Mode=TwoWay}" ItemsSource="{Binding CompareComboboxItem}"
IsEditable="true" IsReadOnly="true"
IsDropDownOpen="True" StaysOpenOnEdit="True"/>
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Column="2" Grid.Row="0" Margin="15,4,6,4" Name="display" VerticalAlignment="Top" Width="120" Text="Choose State"
IsEditable="true" IsReadOnly="true"
IsDropDownOpen="True" StaysOpenOnEdit="True"/>
</Grid>
Attach property
public class SelectionBehavior
{
public static DependencyProperty SelectionChangedProperty =
DependencyProperty.RegisterAttached("SelectionChanged",
typeof(ICommand),
typeof(SelectionBehavior),
new UIPropertyMetadata(SelectionBehavior.SelectedItemChanged));
public static void SetSelectionChanged(DependencyObject target, ICommand value)
{
target.SetValue(SelectionBehavior.SelectionChangedProperty, value);
}
private static void SelectedItemChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
Selector element = target as Selector;
if (element == null) throw new InvalidOperationException("This behavior can be attached to Selector item only.");
if ((e.NewValue != null) && (e.OldValue == null))
{
element.SelectionChanged += SelectionChanged;
}
else if ((e.NewValue == null) && (e.OldValue != null))
{
element.SelectionChanged -= SelectionChanged;
}
}
private static void SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
UIElement element = (UIElement)sender;
ICommand command = (ICommand)element.GetValue(SelectionBehavior.SelectionChangedProperty);
command.Execute(((Selector)sender).SelectedValue);
}
}
viewmodel
public class CompareViewModel
{
private readonly ICommand _selectKeyCmd;
private List<string> _pro_Property;
private List<string> _imp_Property;
private string selectedKey;
private ObservableCollection<string> compareComboboxItem;
private DataTable _dt;
private CompareDiplayData domObject;
private ObservableCollection<CompareDiplayData> _displayData;
public CompareViewModel()
{
_displayData = new ObservableCollection<CompareDiplayData>();
_selectKeyCmd = new RelayCommand(ComboboxChanged, ComboboxIsChanged);
}
private void ReslutData()
{_displayData =null;
if (SelectedKey != null && SelectedComparedData != null)
{
foreach (DataRow pdr in _projectDt.Rows)
{
CompareDiplayData cdd = new CompareDiplayData();
foreach (DataRow idr in _importDt.Rows)
{
if (pdr[SelectedKey].ToString() == idr[SelectedKey].ToString())
{
cdd.Key = pdr[SelectedKey].ToString();
cdd.ProjectData = pdr[SelectedComparedData].ToString();
cdd.ImportData = idr[SelectedComparedData].ToString();
if (pdr[SelectedComparedData].ToString() == idr[SelectedComparedData].ToString())
cdd.State = "Match";
else
cdd.State = "Mismatch";
_displayData.Add(cdd);
}
}
}
}
}
public ObservableCollection<CompareDiplayData> DisplayData
{
get { return _displayData; }
}
public ICommand SelectKeyCmd
{
get { return _selectKeyCmd; }
}
private void ComboboxChanged(object obj)
{
ReslutData();
}
private bool ComboboxIsChanged(object obj)
{
return true;
}`}`
You are updating the reference of the Observablecollection here so that needs to be notified as the PropertyChange. In order to fix this, you will have to implement the INotifyPropertyChanged on your ViewModel also and write the setter of the DisplayData and raise property change for it.
public ObservableCollection<CompareDiplayData> DisplayData
{
get { return _displayData; }
set{_displayData = value;
this.Changed("DisplayData");
}
and in your ReslutData functin instead of update the variable _displayData, update the Property DisplayData.

WPF Control losing focus when clicking on a tab

On a tabcontrol I have several tabpages, on one of the tabs there is a textbox in the content.
This textbox is content bound with a simple Path=PropertyName and UpdateSourceTrigger=LostFocus. The reason I am using LostFocus is I trap the Lost focus event of the Textbox and possibly reformat the text. This is a "time" textbox and if they enter "0900", I want to reformat to "09:00". This part works great when I press the tab key to move to the next control, but if I type "0900" then press one of the other tabs, I hit the lost focus and re-format the value in the textbox, BUT the bind never gets called to update my object. When I come back to the tab, the value is blanked out (or reset to the original value on the object)
Any ideas why textbox does not trigger the Binding update when changing tab page?
Note: this also happens with a regular textbox that does wire to the lost focus event. It seems to have something to do with click on the tab.
[[Added Code ]]
More notes:
1. I am dynamically creating the tabs and controls on the tab (not sure if that has something to do with it or not)
2. I am using the Prism libraries
MainWindow Xaml
<Window.Resources>
<DataTemplate DataType="{x:Type ctrls:myTextBoxDef}">
<Grid Width="300">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding LabelText}" />
<TextBox Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Text="{Binding DocValue,
Mode=TwoWay,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=LostFocus}"
/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
IsTabStop="False"
ItemsSource="{Binding Tabs, Mode=OneWay}"
SelectedItem="{Binding SelectedTab,
Mode=TwoWay,
NotifyOnSourceUpdated=True}"
>
<TabControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Margin="18,14,22,0"
Text="{Binding HeaderText}" />
</Grid>
</DataTemplate>
</TabControl.ItemTemplate>
<!-- Content -->
<TabControl.ContentTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<AdornerDecorator Grid.Column="0">
<ItemsControl Grid.Column="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsTabStop="False"
ItemsSource="{Binding Controls,
Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Grid.Column="0"
Margin="10,5,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</AdornerDecorator>
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
Main Window Code Behind
public partial class MainWindow : Window
{
private DataContextObject obj = new DataContextObject();
public MainWindow()
{
InitializeComponent();
myTextBoxDef txt1 = new myTextBoxDef(obj, "Textbox 1", "TAB1TextBox1");
myTextBoxDef txt1b = new myTextBoxDef(obj, "Textbox 1 value", "TAB1TextBox1");
myTextBoxDef txt2 = new myTextBoxDef(obj, "Textbox 2", "TAB1TextBox2");
myTextBoxDef txt2b = new myTextBoxDef(obj, "Textbox 2 value", "TAB1TextBox2");
obj.Tabs.Add(new myTabDef("Tab 1", new ObservableCollection<myTextBoxDef>() { txt1, txt2 }));
obj.Tabs.Add(new myTabDef("Tab 2", new ObservableCollection<myTextBoxDef>() { txt1b, txt2b }));
obj.SelectedTab = obj.Tabs[0];
this.DataContext = obj;
}
}
Supporting objects
public class DataContextObject : NotificationObject
{
List<myTabDef> _tabs = new List<myTabDef>();
public List<myTabDef> Tabs
{
get
{
return _tabs;
}
}
private myTabDef _item;
public myTabDef SelectedTab
{
get
{ return _item; }
set
{
_item = value;
this.RaisePropertyChanged("SelectedItem");
}
}
private string _txt1 = "";
public string TAB1TextBox1
{
get { return _txt1; }
set
{
_txt1 = value;
this.RaisePropertyChanged("TAB1TextBox1");
}
}
private string _txt2 = "";
public string TAB1TextBox2
{
get { return _txt2; }
set
{
_txt2 = value;
this.RaisePropertyChanged("TAB1TextBox2");
}
}
private string _txt3 = "";
public string TAB2TextBox1
{
get { return _txt3; }
set
{
_txt3 = value;
this.RaisePropertyChanged("TAB2TextBox1");
}
}
}
public class myTabDef
{
public myTabDef(string tabText, ObservableCollection<myTextBoxDef> controls)
{
HeaderText = tabText;
_left = controls;
}
public string HeaderText { get; set; }
private ObservableCollection<myTextBoxDef> _left = new ObservableCollection<myTextBoxDef>();
public ObservableCollection<myTextBoxDef> Controls
{
get
{
return _left;
}
}
}
public class myTextBoxDef : NotificationObject
{
public myTextBoxDef(NotificationObject bound, string label, string bindingPath)
{
LabelText = label;
Path = bindingPath;
BoundObject = bound;
BoundObject.PropertyChanged += BoundObject_PropertyChanged;
}
public string LabelText
{
get;
set;
}
public NotificationObject BoundObject
{
get;
set;
}
public string DocValue
{
get
{
return PropInfo.GetValue(BoundObject, null) as string;
}
set
{
PropInfo.SetValue(BoundObject, value, null);
}
}
protected virtual void BoundObject_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName.Equals(Path))
{
this.RaisePropertyChanged("DocValue");
}
}
public string Path
{
get;
set;
}
private PropertyInfo pi = null;
protected PropertyInfo PropInfo
{
get
{
if (pi == null && BoundObject != null && !string.IsNullOrEmpty(Path))
{
PropertyInfo[] properties = BoundObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
pi = properties.Where((prop) => string.Compare(prop.Name, Path, true) == 0).FirstOrDefault();
}
return pi;
}
}
}
We have found a solution. I came cross this set of postings
https://groups.google.com/forum/#!topic/wpf-disciples/HKUU61A5l74
They talk about a control called TabControlEx. Towards the bottom (5th from the bottom) you will see a posting by Sacha Barber that has a zip file with an example.
It solved all our problems we were having.
here is also another link where the code for the Class is posted
http://updatecontrols.codeplex.com/discussions/214434

Combobox inside datagrid selected item

I have a datagrid which has a column of comboboxes.
The data grid itemssource is a collection of UserInfo objects.
Here's the definition of UserInfo class:
public class UserInfo
{
public string User { get; set; }
public UserRole Role { get; set; }
}
public enum UserRole
{
None = 0,
Administrator = 1,
Reviewer = 2,
}
When I have the collection, I assign it to the datagrid:
private void svc_GetAllUsersCompleted(object sender, ServiceReference1.GetAllUsersCompletedEventArgs args)
{
ObservableCollection<UserInfo> users = args.Result;
UsersPage.dataGrid1.ItemsSource = users;
}
Here's the xaml of the datagrid:
<data:DataGrid Margin="5,25,5,17" AutoGenerateColumns="False" AllowDrop="True" Name="dataGrid1" SelectionMode="Single" UseLayoutRounding="True" SelectionChanged="dataGrid1_SelectionChanged" Grid.RowSpan="2" Grid.ColumnSpan="2" Grid.Row="1" ItemsSource="{Binding}" >
<data:DataGrid.Resources>
<DataTemplate x:Key="UserRoleTemplate">
<Border BorderThickness="0,0,0,0" BorderBrush="#6FBDE8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ComboBox Name="cmbUserRoleTypes" VerticalAlignment="Center" Grid.Column="0" Loaded="cmbUserRoleTypes_Loaded" SelectedIndex="0" ItemsSource="{Binding GetListOfRoles,Source={StaticResource rList}}" SelectedValue="{Binding Role, Mode=TwoWay}" ></ComboBox>
</Grid>
</Border>
</DataTemplate>
<DataTemplate x:Key="UserNameTemplate">
<Border BorderThickness="0,0,0,0" BorderBrush="#6FBDE8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Name="txtUserName" VerticalAlignment="Center" Grid.Column="0" Loaded="cmbUserRoleTypes_Loaded" Text="{Binding Path=Name}" ></TextBlock>
</Grid>
</Border>
</DataTemplate>
</data:DataGrid.Resources>
<data:DataGrid.Columns>
<data:DataGridTextColumn Header="User Name" Width="200"
Binding="{Binding User}" />
<data:DataGridTemplateColumn Header="User Role" Width="200"
CellTemplate="{StaticResource UserRoleTemplate}" />
<!--<data:DataGridTextColumn Header="Assigned Issues" />-->
</data:DataGrid.Columns>
</data:DataGrid>
The combo is filled using a collection from a class that has all the users roles:here's the xaml:
<UserControl.Resources>
<local:RolesTypes x:Key="rList">
</local:RolesTypes>
</UserControl:Resources>
And here's the class that has the collection:
public class RolesTypes
{
public List<string> GetListOfRoles
{
get
{
List<string> RolesList = new List<string>();
RolesList.Add("administrator");
RolesList.Add("reviewer");
return RolesList;
}
}
}
My problem is:
The combo fills fine with the list of roles, but when I receive the usersinfo collection, I want each user to have its role selected in its matching combo and it doesn't happen. no role is selected in the combo, although the users roles DO exist in the list of roles .
Any ideas?
CAVEAT: this populates the grid with a combobox and sets the combobox to the users' role. It's done in code behind, which I think violates all the MVVM principals, but I couldn't get binding to work. (Maybe some binding expert could modify this) That said, if you go with it you should probably attach a handler to the combobox to update you're user's role when the combo box is changed. Hope this helps and good luck!
REVISED cmbUserRoleTypes_Loaded to populate combo box, and removed converter code. Note that the different role values are hard coded, you probably want to make that generic.
REVISED to include combo box, sorry was rushing to finish before I had to leave and didn't re-read your post. I don't really like that it has to set the combo box in code-behind, it seems like there should be some way to data bind it. NOTE: I'm having trouble with binding the combo box selection to the user record, but at least this gets the populated combo boxes in there. Hope it helps.
Here is the xaml
<UserControl xmlns:data="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="StackOverflowProblems.MainPage"
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:local="clr-namespace:StackOverflowProblems"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Content="Populate users" Click="btn_Click" HorizontalAlignment="Left"></Button>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<data:Label Content="Roles:"/>
<ComboBox VerticalAlignment="Center" Name="myComboBox" ></ComboBox>
</StackPanel>
<Grid Grid.Row="2" x:Name="LayoutRoot" Background="White">
<data:DataGrid Margin="5,25,5,17" AutoGenerateColumns="False" AllowDrop="True" Name="dataGrid1" SelectionMode="Single" UseLayoutRounding="True" SelectionChanged="dataGrid1_SelectionChanged" Grid.RowSpan="2" Grid.ColumnSpan="2" Grid.Row="1" ItemsSource="{Binding}" >
<data:DataGrid.Resources>
<DataTemplate x:Key="UserRoleTemplate">
<Border BorderThickness="0,0,0,0" BorderBrush="#6FBDE8">
<ComboBox VerticalAlignment="Center" Loaded="cmbUserRoleTypes_Loaded" >
</ComboBox>
</Border>
</DataTemplate>
<DataTemplate x:Key="UserNameTemplate">
<Border BorderThickness="0,0,0,0" BorderBrush="#6FBDE8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Name="txtUserName" VerticalAlignment="Center" Grid.Column="0" Loaded="cmbUserRoleTypes_Loaded" Text="{Binding Path=Name}" ></TextBlock>
</Grid>
</Border>
</DataTemplate>
</data:DataGrid.Resources>
<data:DataGrid.Columns>
<data:DataGridTextColumn Header="User Name" Width="200" Binding="{Binding User}" />
<data:DataGridTemplateColumn Header="User Role" Width="200" CellTemplate="{StaticResource UserRoleTemplate}" />
</data:DataGrid.Columns>
</data:DataGrid>
</Grid>
</Grid>
</UserControl>
Here is the code behind
using System;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
namespace StackOverflowProblems
{
public partial class MainPage : UserControl
{
ObservableCollection<UserInfo> users = new ObservableCollection<UserInfo>();
ObservableCollection<string> roles = new ObservableCollection<string>();
public MainPage()
{
InitializeComponent();
LayoutRoot.DataContext = this;
InitializeRoles();
}
public void InitializeRoles()
{
// turn enumeration into a collection of strings
Type enumType = typeof(UserRole);
foreach (FieldInfo fieldInfo in enumType.GetFields(BindingFlags.Public | BindingFlags.Static))
{
roles.Add(fieldInfo.Name.ToString());
}
myComboBox.ItemsSource = roles;
myComboBox.SelectedIndex = 0;
}
public void svc_GetAllUsersCompleted()
{
users.Add(new UserInfo("Fred", UserRole.Administrator));
users.Add(new UserInfo("George", UserRole.None));
users.Add(new UserInfo("Mary", UserRole.Reviewer));
dataGrid1.ItemsSource = users;
}
private void dataGrid1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
private void cmbUserRoleTypes_Loaded(object sender, RoutedEventArgs e)
{
ComboBox bx = (ComboBox)sender;
UserInfo ui = (UserInfo)bx.Tag;
bx.ItemsSource = roles;
int userRoleIndex = 0;
switch (ui.Role)
{
case UserRole.None:
userRoleIndex = 0;
break;
case UserRole.Administrator:
userRoleIndex = 1;
break;
case UserRole.Reviewer:
userRoleIndex = 2;
break;
default:
throw new Exception("Invalid Role Detected");
}
bx.SelectedIndex = userRoleIndex;
}
private void btn_Click(object sender, RoutedEventArgs e)
{
svc_GetAllUsersCompleted();
}
}
}
Here is the supporting class file
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Data;
namespace StackOverflowProblems
{
public class UserInfo : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private string _User = "";
public string User
{
get { return _User; }
set
{
if (_User != value)
{
_User = value;
NotifyPropertyChanged("User");
}
}
}
private UserRole _Role = UserRole.None;
public UserRole Role
{
get { return _Role; }
set
{
if (_Role != value)
{
_Role = value;
NotifyPropertyChanged("User");
}
}
}
public UserInfo(string user, UserRole role)
{
User = user;
Role = role;
}
}
public enum UserRole
{
None = 0,
Administrator = 1,
Reviewer = 2,
}
}
It is also very possible for your SelectedValue to receive it's value before the ItemsSource gets its value for GetListOfRoles.
If I remember right, that can cause issues with the 'value' being set to an item that does not exist in the ItemsSource yet.

Resources