styling nth item in listview? - wpf

I have an external 'current' property (int) which represents the current index of a collection. I have a listview that displays this collection. I want to be able to style the 'nth' item of the collection depending on the value of 'current', i.e. if current is 3, highlight the 4th item in the collection (index = 3), etc. How can I do this?
An alternative would be to bind when the item text is equal to another external property.

To customize the style of a ListView, you can create a DataTrigger that binds to a property of the View's DataContext to alter the current style. In this example the code alters the Background.
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Background" Value="Aqua"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.StyleType, RelativeSource={RelativeSource Self}}" Value="1">
<Setter Property="Background" Value="Chartreuse"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
I've added most of the code here so you can get as complete picture as possible of what is happening, but I skipped the common MVVM base classes.
How it works:
ListView ItemsSource binds to Customers
ListView SelectedItem binds to Customer
ListView.ItemContainerStyle has a DataTrigger that binds to DataContext.StyleType
DataContext is a List of Customer objects with a user defined StyleType property that gets initialized in the code-behind
A Button Command clears the value of StyleType
Clicking a row changes the style of the next row
Customer implements INotifyPropertyChanged, that fires when StyleType changes
DataTrigger alters the Background each ListViewItem
Here is the XAML, look at DataTrigger:
<Window x:Class="ListViewScrollPosition.Views.ScrollBarwindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Style on Model" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListView
Grid.Row="0"
SelectedItem="{Binding Customer}"
ItemsSource="{Binding Customers}" x:Name="myListView">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Background" Value="Aqua"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext.StyleType, RelativeSource={RelativeSource Self}}" Value="1">
<Setter Property="Background" Value="Pink"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="First Name"
DisplayMemberBinding="{Binding FirstName}" />
<GridViewColumn Header="Last Name"
DisplayMemberBinding="{Binding LastName}" />
<GridViewColumn Header="Style Type"
DisplayMemberBinding="{Binding StyleType}" />
</GridView>
</ListView.View>
</ListView>
<Button Grid.Row="1" Content="Change Style" Command="{Binding Path=AlterStyle}"/>
</Grid>
</Window>
Here is the ViewModel the View uses to get Customers and to alter the value of StyleType:
using System.Collections.Generic;
using System.Windows.Input;
using ListViewScrollPosition.Commands;
using ListViewScrollPosition.Models;
namespace ListViewScrollPosition.ViewModels
{
public class MainViewModel : ViewModelBase
{
private DelegateCommand _alterStyleCommand;
public MainViewModel()
{
}
public ICommand AlterStyle
{
get
{
if (_alterStyleCommand == null)
{
_alterStyleCommand = new DelegateCommand(AlterStyleCommand);
}
return _alterStyleCommand;
}
}
private void AlterStyleCommand()
{
foreach (var customer in Customers)
{
customer.StyleType = 0;
}
}
private void ApplyStyleToNextRow(Customer currentCustomer)
{
bool setNext = false;
foreach (var customer in Customers)
{
if (setNext)
{
customer.StyleType = 1;
setNext = false;
}
else
{
customer.StyleType = 0;
}
if (currentCustomer == customer)
{
setNext = true;
}
}
}
private List<Customer> _customers = Customer.GetSampleCustomerList();
public List<Customer> Customers
{
get
{
return _customers;
}
}
private Customer _customer = null;
public Customer Customer
{
get
{
return _customer;
}
set
{
_customer = value;
ApplyStyleToNextRow(_customer);
OnPropertyChanged("Customer");
}
}
}
}
Here is the Model, ViewModelBase implements INotifyPropertyChanged, StyleType fires OnPropertyChanged when changed which updates each ListViewItem:
using System;
using System.Collections.Generic;
namespace ListViewScrollPosition.Models
{
public class Customer : ViewModels.ViewModelBase
{
public String FirstName { get; set; }
public String LastName { get; set; }
private int _style;
public int StyleType
{
get { return _style;}
set
{
_style = value;
OnPropertyChanged("StyleType");
}
}
public Customer(String firstName, String lastName, int styleType)
{
this.FirstName = firstName;
this.LastName = lastName;
this.StyleType = styleType;
}
public static List<Customer> GetSampleCustomerList()
{
return new List<Customer>(new Customer[4] {
new Customer("A.", "Zero", 0),
new Customer("B.", "One", 1),
new Customer("C.", "Two", 2),
new Customer("D.", "Three", 1)
});
}
}
}
Here is the code-behind where I setup the DataContext:
using System.Windows;
namespace ListViewScrollPosition.Views
{
public partial class ScrollBarwindow : Window
{
public ScrollBarwindow()
{
InitializeComponent();
DataContext = new ViewModels.MainViewModel();
}
}
}

Related

In WPF,How to add radio button , checkbox, comboBox based on the type which is configured in the database?

Totally i am new to WPF, I need to solve the problem. Could anybody give me a sample xaml code without using code-behind.
Based on the questType (Check,radio, combo) Control need to be created based on the ObserverableCollection.
public class Order
{
public int OrderCode {get;set;}
public string Description {get;set;}
public ObserverableCollection<question> Questions{get;set;}
}
public class question
{
public string questType {get;set;}
public string Question {get;set;}
public ObserverableCollection<Answer> Answers {get;set;}
}
public class Answer
{
public string Ans{get; set;}
}
Based on the questType (Check,radio, combo)
Control need to be created based on the ObserverableCollection.
example:
1001 Pencil Gender? oMale oFemale oOther
[]Checkbox1 []Checkbox2
1002 Pen Fasting? oYes oNo
Here is how I would do it:
Code behind:
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
namespace Ans
{
public class Order
{
public int OrderCode { get; set; }
public string Description { get; set; }
public ObservableCollection<Question> Questions { get; set; }
}
public class Question
{
public string questType { get; set; }
public string Label { get; set; }
public ObservableCollection<Answer> Answers { get; set; }
}
public class Answer
{
public string Ans { get; set; }
public bool IsSelected { get; set; }
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
#region Order
/// <summary>
/// Order Dependency Property
/// </summary>
public static readonly DependencyProperty OrderProperty =
DependencyProperty.Register("Order", typeof(Order), typeof(MainWindow),
new FrameworkPropertyMetadata((Order)null));
/// <summary>
/// Gets or sets the Order property. This dependency property
/// indicates ....
/// </summary>
public Order Order
{
get { return (Order)GetValue(OrderProperty); }
set { SetValue(OrderProperty, value); }
}
#endregion
public MainWindow()
{
InitializeComponent();
Order = new Order()
{
Questions = new ObservableCollection<Question>()
{
new Question()
{
questType = "Combo",
Label = "Combo",
Answers = new ObservableCollection<Answer>()
{
new Answer(){Ans = "Female"},
new Answer(){Ans = "Male"}
}
},
new Question()
{
questType = "Check",
Label = "Multi",
Answers = new ObservableCollection<Answer>()
{
new Answer(){Ans = "Female"},
new Answer(){Ans = "Male"}
}
},
new Question()
{
questType = "Radio",
Label = "Radio",
Answers = new ObservableCollection<Answer>()
{
new Answer(){Ans = "Female"},
new Answer(){Ans = "Male"}
}
}
}
};
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach(Question q in Order.Questions)
{
Console.WriteLine( q.Label + " : " + string.Join(", " , q.Answers.Where(a=>a.IsSelected).Select(a=>a.Ans)) );
}
}
}
}
XAML:
<Window x:Class="Ans.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:Ans"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate x:Key="ComboQuestion">
<ComboBox ItemsSource="{Binding Answers}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Ans}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</DataTemplate>
<DataTemplate x:Key="CheckQuestion">
<ItemsControl ItemsSource="{Binding Answers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Ans}" IsChecked="{Binding IsSelected, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
<DataTemplate x:Key="RadioQuestion">
<ItemsControl ItemsSource="{Binding Answers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Ans}" IsChecked="{Binding IsSelected, Mode=TwoWay}" GroupName="{Binding DataContext.Label, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Order.Questions}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Label"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Label}"/>
<ContentControl x:Name="ccQuestion" Grid.Column="1" Content="{Binding}" Margin="10"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding questType}" Value="Combo">
<Setter TargetName="ccQuestion" Property="ContentTemplate" Value="{StaticResource ComboQuestion}"/>
</DataTrigger>
<DataTrigger Binding="{Binding questType}" Value="Check">
<Setter TargetName="ccQuestion" Property="ContentTemplate" Value="{StaticResource CheckQuestion}"/>
</DataTrigger>
<DataTrigger Binding="{Binding questType}" Value="Radio">
<Setter TargetName="ccQuestion" Property="ContentTemplate" Value="{StaticResource RadioQuestion}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Order" Click="Button_Click" VerticalAlignment="Bottom"/>
</Grid>
</Window>
The only thing I've added to your model is IsSelected property that allows to know if this answer was selected.
The other important thing is the Radios. Their GroupName property defines the scope. So if no GroupName is set then when clicking on a radio in one question it will unselect radio in another question. I used Question label in my solution however it only works if labels are unique.
Another point is that data triggers are OK if you have 3-5 question types and if thay are only based on the questionType. However for more complex scenarios you can look for ItemTemplateSelector. It allows to write C# code that will select the template based on each item in ItemsControl.

How to use GridViewComboBoxColumn and allow the user to edit?

I need to present a WPF GridView where one column is a Combobox, The user can select one value from the list or enter a new value so I set the IsComboBoxEditable to true but the problem is that if the user types a value that is not in the ItemsSource the Text is blank when the Combobox looses the focus.
Note : I don't want, when a new value is typed , this value to be
added to the ItemsSource. I only need to save it's string value in row
that bounded to it.
I also need DropDownOpened event, to populate it's ItemsSource.
Here is my code:
<telerik:GridViewDataColumn Header="Description">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<telerik:RadComboBox IsEditable="True" ItemsSource="{Binding Descriptions}" Text="{Binding Description1,Mode=TwoWay}" DropDownOpened="descriptionRadComboBox_DropDownOpened"/>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
Description1 is string property, and Descriptions is List of string that populate in runtime.(When DropDownOpened Event occurred)
Like you mentioned, your goal is, simply, to "Editable ComboBox".
(And, of course, you don't want to add new Item to ItemsSource)
<telerik:GridViewDataColumn UniqueName="description1" Header="Description">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description1}"></TextBlock>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
<telerik:GridViewDataColumn.CellEditTemplate>
<DataTemplate>
<telerik:RadComboBox Name="SLStandardDescriptionsRadComboBox" IsEditable="True"
ItemsSource="{Binding DataContext.SLStandardDescriptions, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
DisplayMemberPath="SLStandardDescriptionTitle" DropDownOpened="Description_DropDownOpened">
</telerik:RadComboBox>
</DataTemplate>
</telerik:GridViewDataColumn.CellEditTemplate>
</telerik:GridViewDataColumn>
Codebehinde :
private void RadGridView_CellEditEnded(object sender, GridViewCellEditEndedEventArgs e)
{
if (e.Cell.Column.UniqueName == "description1")
{
RadComboBox combo = e.Cell.ChildrenOfType<RadComboBox>().FirstOrDefault();
if (combo != null)
{
List<Description> comboItems = combo.ItemsSource as List<Description>;
string textEntered = e.Cell.ChildrenOfType<RadComboBox>().First().Text;
bool result = comboItems.Contains(comboItems.Where(x => x.DescriptionTitle == textEntered).FirstOrDefault());
if (!result)
{
comboItems.Add(new Description { DescriptionTitle = textEntered });
combo.SelectedItem = new Description { DescriptionTitle = textEntered };
}
if (_viewModel.AccDocumentItem != null)
{
if (e.Cell.Column.UniqueName == "description1")
_viewModel.AccDocumentItem.Description1 = textEntered;
}
}
}
}
Here is the solution for .net DataGrid control:
<DataGrid ItemsSource="{Binding Path=Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Title}" ></DataGridTextColumn>
<DataGridComboBoxColumn SelectedValueBinding="{Binding ComboItem.ID}" DisplayMemberPath="ComboTitle" SelectedValuePath="ID">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.ComboItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.ComboItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
<Setter Property="IsEditable" Value="True" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
Surely you can do this using Telerik DataGrid control as well.
And here is my ViewModel:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
ComboItems = new ObservableCollection<ComboItem>()
{
new ComboItem(){ID=1,ComboTitle="ComboItem1"},
new ComboItem(){ID=2,ComboTitle="ComboItem2"},
new ComboItem(){ID=3,ComboTitle="ComboItem3"}
};
Items = new ObservableCollection<Item>()
{
new Item(){ID=1,Title="Item1",ComboItem=ComboItems[0]},
new Item(){ID=2,Title="Item2",ComboItem=ComboItems[1]},
new Item(){ID=3,Title="Item3",ComboItem=ComboItems[2]}
};
}
public ObservableCollection<Item> Items { get; set; }
public ObservableCollection<ComboItem> ComboItems { get; set; }
}
public class Item
{
public int ID { get; set; }
public string Title { get; set; }
public ComboItem ComboItem { get; set; }
}
public class ComboItem
{
public int ID { get; set; }
public string ComboTitle { get; set; }
}

Using DataGridComboBoxColumn as autocompletecombobox in a DataGrid

I want to use the DataGridComboBoxColumn as a autocomplete combobox.
I've got it partially working. When the Row is in EditMode I can type text in the ComboBox, also in ViewMode the control returns the text. Only how to get the Label (in template) to EditMode by mouse doubleclick?
Up front, I don't want to use the DataGridTemplateColumn control because it just doesn't handle keyboard and mouse entry like the DataGridComboBoxColumn does (tabs, arrows, edit/view mode/ double click etc..).
It looks like:
I fixed it adding a behavior to the TextBox to get a link to the parent DataGrid then setting the Row into Edit Mode by calling BeginEdit().
The solution I used:
View
<Window x:Class="WpfApplication1.MainWindow"
xmlns:local="clr-namespace:WpfApplication1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Model.Things}" Name="MyGrid" ClipboardCopyMode="IncludeHeader">
<DataGrid.Resources>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Object" MinWidth="140" TextBinding="{Binding ObjectText}" ItemsSource="{Binding Source={StaticResource proxy}, Path=Data.Model.ObjectList}" >
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsEditable" Value="True"/>
<Setter Property="Text" Value="{Binding ObjectText}"/>
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBox IsReadOnly="True" Text="{Binding Path=DataContext.ObjectText, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}}">
<TextBox.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="local:CellSelectedBehavior.IsCellRowSelected" Value="true"></Setter>
</Style>
</TextBox.Resources>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.ElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Model
public class Model : BaseModel
{
//List of objects for combobox
private List<string> _objectList;
public List<string> ObjectList { get { return _objectList; } set { _objectList = value; } }
//Rows in datagrid
private List<Thing> _things;
public List<Thing> Things
{
get { return _things; }
set { _things = value; OnPropertyChanged("Things"); }
}
}
public class Thing : BaseModel
{
//Text in combobox
private string _objectText;
public string ObjectText
{
get { return _objectText; }
set { _objectText = value; OnPropertyChanged("ObjectText"); }
}
}
ViewModel
public class ViewModel
{
public Model Model { get; set; }
public ViewModel()
{
Model = new WpfApplication1.Model();
Model.ObjectList = new List<string>();
Model.ObjectList.Add("Aaaaa");
Model.ObjectList.Add("Bbbbb");
Model.ObjectList.Add("Ccccc");
Model.Things = new List<Thing>();
Model.Things.Add(new Thing() { ObjectText = "Aaaaa" });
}
}
Behavior
public class CellSelectedBehavior
{
public static bool GetIsCellRowSelected(DependencyObject obj) { return (bool)obj.GetValue(IsCellRowSelectedProperty); }
public static void SetIsCellRowSelected(DependencyObject obj, bool value) { obj.SetValue(IsCellRowSelectedProperty, value); }
public static readonly DependencyProperty IsCellRowSelectedProperty = DependencyProperty.RegisterAttached("IsCellRowSelected",
typeof(bool), typeof(CellSelectedBehavior), new UIPropertyMetadata(false, OnIsCellRowSelected));
static void OnIsCellRowSelected(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
TextBox item = depObj as TextBox;
if (item == null)
return;
if (e.NewValue is bool == false)
return;
if ((bool)e.NewValue)
item.MouseDoubleClick += SelectRow;
else
item.MouseDoubleClick -= SelectRow;
}
static void SelectRow(object sender, EventArgs e)
{
TextBox box = sender as TextBox;
var grid = box.FindAncestor<DataGrid>();
grid.BeginEdit();
}
}
Helper (to find DataGrid)
public static class Helper
{
public static T FindAncestor<T>(this DependencyObject current) where T : DependencyObject
{
current = VisualTreeHelper.GetParent(current);
while (current != null)
{
if (current is T)
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
};
return null;
}
}

Binding a WPF DataGridComboBoxColumn with MVVM

I've looked at the answers to various questions, but haven't managed to map the content in the answers to the problem I'm attempting to solve. I've reduced it down to the following code (representative of the outcome I'm trying to achieve), and basically want to be able to render the Person.TitleId as its corresponding Title.TitleText when the row isn't being edited, and have the drop-down bound correctly so that it displays the TitleTexts in the drop-down and writes the associated TitleId back to the Person record when its changed.
In short, what do I put in my <DataGridComboBoxColumn> to achieve this?
App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var viewModel = new ViewModels.MainWindowViewModel();
var mainWindow = new MainWindow();
mainWindow.DataContext = viewModel;
mainWindow.ShowDialog();
}
MainWindow.xaml
<Grid>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Path=Contacts}">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Title" SelectedItemBinding="{Binding Person}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/>
<Setter Property="IsReadOnly" Value="True"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/>
<Setter Property="DisplayMemberPath" Value="TitleText" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Person.cs
public class Person
{
public int TitleId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
Title.cs
public struct Title
{
public Title(int titleId, string titleText)
: this()
{
TitleId = titleId;
TitleText = titleText;
}
public string TitleText { get; private set; }
public int TitleId { get; private set; }
public static List<Title> GetAvailableTitles()
{
var titles = new List<Title>();
titles.Add(new Title(1, "Mr"));
titles.Add(new Title(2, "Miss"));
titles.Add(new Title(3, "Mrs"));
return titles;
}
}
MainWindowViewModel.cs
public class MainWindowViewModel : ViewModelBase
{
private ObservableCollection<Person> contacts;
private List<Title> titles;
public MainWindowViewModel()
{
titles = Title.GetAvailableTitles();
Contacts = new ObservableCollection<Person>();
Contacts.Add(new Person() { FirstName = "Jane", LastName = "Smith", TitleId = 2 });
}
public List<Title> Titles
{
get { return titles; }
}
public ObservableCollection<Person> Contacts
{
get { return contacts; }
set
{
if (contacts != value)
{
contacts = value;
this.OnPropertyChanged("Contacts");
}
}
}
}
ViewModelBase.cs
public class ViewModelBase : INotifyPropertyChanged
{
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Here is a working code. The key point here was to use SelectedValueBinding instead of SelecteItemBinding.
<DataGridComboBoxColumn Header="Title"
SelectedValueBinding="{Binding TitleId}"
SelectedValuePath="TitleId"
DisplayMemberPath="TitleText"
>
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
#SnowBear's answer worked well for me. But I want to clarify a detail of the binding.
In #Rob's example, both Title and Person classes use TitleID.
Therefore, in #SnowBear's answer, in the binding:
SelectedValueBinding="{Binding TitleId}"
it wasn't immediately obvious to me which class and property was being bound.
Because the SelectedValueBinding attribute appeared on the DataGridComboBoxColumn, it is binding to the ItemsSource of the containing DataGrid. In this case the Contacts collection of Person objects.
In my case, the DataGrid's DataSource collection was attributed with a property that was named different from the ValuePath of the ComboBox's ItemSource collection. So my SelectedValueBinding's value was bound to a different property than the property named in the ComboBox's SelectedValuePath.

Changing the User Control of Window dynamically in WPF (MVVM)

I am working on wpf mvvm pattern.
I have a user control in which i am loading a list of checkboxes in DataGridCheckBoxColumn and binding it to IsSelected property from my viewmodel.
Tha xaml code is like this:
<DataGrid Width="150" Grid.Row="0" Background="LightGray" CanUserAddRows="False" AutoGenerateColumns="False" HorizontalAlignment="Left" Name="dataGridCustomers" ItemsSource="{Binding Path=UsecaseListItems}" CanUserResizeColumns="False" CanUserResizeRows="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding Path=IsSelected,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay,IsAsync=True}" Width="50">
<DataGridCheckBoxColumn.HeaderTemplate>
<DataTemplate x:Name="dtAllChkBx">
<CheckBox Name="cbxAll" FontWeight="Bold" Content="All" IsChecked="{Binding Path=DataContext.AllSelected,RelativeSource={RelativeSource AncestorType=UserControl },Mode=TwoWay}"/>
</DataTemplate>
</DataGridCheckBoxColumn.HeaderTemplate>
</DataGridCheckBoxColumn>
<DataGridTextColumn Width="85" Binding="{Binding Path=UsecaseName}" Header="UsecaseName" IsReadOnly="True" >
<DataGridColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Black"></Setter>
</Style>
</DataGridColumn.HeaderStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
My HomeViewModel is like this:
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set
{
_IsSelected = value;
OnPropertyChanged("IsSelected");
}
}
private Control _someUserControl;
public Control CCS01
{
get { return _someUserControl; }
set { _someUserControl = value; }
}
private UserControl _content;
internal void SetNewContent(UserControl _content)
{
ContentWindow = _content;
}
public UserControl ContentWindow
{
get { return _content; }
set
{
_content = value;
OnPropertyChanged("ContentWindow");
}
}
For my 1st checkbox (CCS01), I have created a view(CCS01.xaml) which contains a few labels and textboxes in grid format.And its corresponding ViewModel is below:
public class CCS01ViewModel: BaseNotifyPropertyChanged
{
HomeViewModel _homeViewModel;
public ICommand OpenUsersCommand { get; private set; }
public CCS01ViewModel(HomeViewModel mainModel)
{
this._homeViewModel = mainModel;
//this._model = model;
OpenUsersCommand = new RelayCommand(OpenUsers, CanOpenUsers);
}
private void OpenUsers(object _param)
{
//UsersPanelViewModel upmodel = new UsersPanelViewModel(_mainModel, _model);
//UsersPanel up = new UsersPanel();
//up.DataContext = upmodel;
//_mainModel.SetNewContent(up);
}
private bool CanOpenUsers(object _param)
{
return true;
}
}
I want to load the selected checkbox view in the main UserControl(ExecutionDetails.xaml) . Currently, I am loading it with the first Checkbox view in it by default like this:
<StackPanel>
<Grid Name="HostGrid">
<ContentControl Content="{Binding ContentWindow}"/>
</Grid>
</StackPanel>
Please suggest me how to bind the view with respective checkboxes user controls dynamically based on the checkbox selection.
The code behind of this(ExecutionDetails.xaml.cs) is like this:
public partial class ExecutionDetails : UserControl
{
public ExecutionDetails()
{
InitializeComponent();
HomeViewModel viewmodel = new HomeViewModel();
CCS01ViewModel ccViewModel = new CCS01ViewModel(viewmodel);
CCS01 obj = new CCS01();
obj.DataContext = ccViewModel;
viewmodel.ContentWindow = obj;
this.DataContext = viewmodel;
}
}

Resources