Xceed datagrid showing combobox - c#

I wanted to use "Xceed DataGrid for WPF" community edition to show items in WPF application that are already coming correct for a Propertygrid.
The textbox (first name) is working fine, but combobox is not working. Problem is combobox is not populating anything and it is not setting the gender correctly. My simple code is given below.
XAML:
<Window.Resources>
<xcdg:DataGridCollectionViewSource x:Key="mySource" Source="{Binding Path=SelectedEntity}" />
</Window.Resources>
<Grid>
<xcdg:DataGridControl ItemsSource="{Binding Source={StaticResource mySource}}" AutoCreateColumns="True">
<xcdg:DataGridControl.Columns>
<xcdg:Column FieldName="FirstName" Title="First Name" />
<xcdg:Column FieldName="Gender" Title="Gender" >
<xcdg:Column.CellContentTemplate>
<DataTemplate x:Name="clmGenderTmp">
<ComboBox SelectedValuePath="GenderID"
DisplayMemberPath="Name"
ItemsSource="{Binding Path=SelectedEntity.Gender, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
SelectedValue="{xcdg:CellEditorBinding}"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
</xcdg:Column>
</xcdg:DataGridControl.Columns>
</xcdg:DataGridControl>
</Grid>
Xaml.cs is:
InitializeComponent();
this.DataContext = new MainWindowViewModel();
Data class is:
using System.ComponentModel;
using Xceed.Wpf.DataGrid;
public enum Genders { Male, Female }
public class Person
{
public Person(string firstName, Genders gender)
{
FirstName = firstName;
Gender = gender;
}
[DisplayName("Given name")]
public string FirstName { get; set; }
[Browsable(true)]
public Genders Gender { get; set; }
}
View Model is:
public class MainWindowViewModel
{
public List<object> SelectedEntity { get; set; }
public MainWindowViewModel()
{
SelectedEntity = new List<object>();
this.SelectedEntity.Add(new Person("Mathew", Genders.Male));
this.SelectedEntity.Add(new Person("Mark", Genders.Female));
}
}

It seems that Window does not have a property SelectedEntity. ;-) What you wanted to bind to is DataContext.SelectedEntity.Gender. Anyway, this will not work at all, because you are trying to bind a single value (Gender) as a ItemsSource.
You need to provide a list of (enum) values to the combobox.
How to bind an enum to a combobox control in WPF?
EDIT
As my answer was obviously not concrete enough, I provide a full example:
XAML:
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Firstname}"
Margin="10" />
<ComboBox ItemsSource="{Binding Path=AvailableGenders}"
SelectedValue="{Binding Path=Gender}"
Grid.Column="1" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
PersonViewModel:
public class PersonViewModel : ViewModelBase
{
private Genders _gender;
private string _firstname;
public string Firstname
{
get
{
return _firstname;
}
set
{
_firstname = value;
OnPropertyChanged();
}
}
public Genders Gender
{
get
{
return _gender;
}
set
{
_gender = value;
OnPropertyChanged();
}
}
public List<Genders> AvailableGenders
{
get
{
return Enum.GetValues(typeof(Genders)).Cast<Genders>().ToList();
}
}
}
MainWindow Ctor:
public MainWindow()
{
InitializeComponent();
List<PersonViewModel> persons = new List<PersonViewModel>();
persons.Add(new PersonViewModel() { Firstname = "John", Gender = Genders.female });
persons.Add(new PersonViewModel() { Firstname = "Partyboy", Gender = Genders.male });
persons.Add(new PersonViewModel() { Firstname = "r2d2", Gender = Genders.robot });
persons.Add(new PersonViewModel() { Firstname = "KlausMaria", Gender = Genders.shemale });
DataContext = persons;
}

I got a similar example from How to add ComboBox column in XCeed DataGridControl (WPF)
Just publishing my code, if anyone faces same issue.
ViewModel:
public class ViewModel
{
public DataTable Persons { get; set; }
public DataTable Gender { get; set; }
public ViewModel()
{
Persons = new DataTable();
Persons.Columns.Add("FirstName", typeof(string));
Persons.Columns.Add("GenderID", typeof(int));
Persons.Rows.Add("Mathew",1);
Persons.Rows.Add("Gil", 2);
Gender = new DataTable();
Gender.Columns.Add("Name", typeof(string));
Gender.Columns.Add("GenderID", typeof(int));
Gender.Rows.Add("Male", 1);
Gender.Rows.Add("Female", 2);
}
}
Code Behind:
InitializeComponent();
viewModel = new ViewModel();
this.DataContext = viewModel;
XMAL:
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type xcdg:GroupByControl}">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
</Grid.Resources>
<xcdg:DataGridControl ItemsSource="{Binding Persons}" >
<xcdg:DataGridControl.Columns>
<xcdg:Column x:Name="clmFN" FieldName="FirstName"/>
<xcdg:Column x:Name="clmGender" FieldName="GenderID">
<xcdg:Column.CellContentTemplate>
<DataTemplate x:Name="clmGenderTmp">
<ComboBox SelectedValuePath="GenderID"
DisplayMemberPath="Name"
ItemsSource="{Binding Path=DataContext.Gender, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
SelectedValue="{xcdg:CellEditorBinding}"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
</xcdg:Column>
</xcdg:DataGridControl.Columns>
</xcdg:DataGridControl>
</Grid>

Related

MessagingCenter not subscribing

I'm new to Xamarin (and new in coding in general).
I'm using xct TouchEffect within a ListView to try to get a LongPress menu.
Since the TouchEffect.LongPressCommand is a Command, I can only bound it to the model page from some reason.
So... I'm trying to send information to the Code-behind via MessagingCenter.
The problem I have is the message is not receiving.
I read a lot and tried to figure it out, and I guess to be able to receive the message, the subscriber needs to be instantiate/initialize first.
The main problem I have is... I don't know how to do it lol.
Or the whole thing I'm trying to do is wrong?
I will add some code but if anything else is needed please let me know.
Note: the loading page (when the app start) is a GroupPage(which working fine), the problem is with the ItemsPage.
Thank you so much for everyone.
ItemsPage.xaml
<ContentPage.BindingContext>
<localvm:ItemViewModel/>
</ContentPage.BindingContext>
<ListView ItemsSource="{Binding Items, Mode=TwoWay}" x:Name="lstView"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
SelectedItem="{Binding SelectedItem}" HasUnevenRows="True" RowHeight="50">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="0,0,8,0" Margin="4,0,4,0" xct:TouchEffect.LongPressCommand="{Binding LongPressItemCommand}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding ItemName}" TextColor="Black" Grid.Column="1" FontSize="Medium"></Label>
<Label Text="{Binding ItemDescription}" Grid.Column="1" VerticalTextAlignment="End"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ItemsPage.cs
namespace MobileApp2
{
public partial class ItemsPage : ContentPage
{
public ItemsPage()
{
InitializeComponent();
MessagingCenter.Subscribe<Item, Guid>(this, "PopupMenuItemMsg",
(page, itemId) =>
{
Main_PopupMenu(itemId);
});
}
public async void Main_PopupMenu(Guid itemId)
{
DisplayActionSheet("Test", "Test", "OK");
}
}
}
Items.cs (model)
namespace MobileApp2
{
public class Item : INotifyPropertyChanged
{
public Command LongPressItemCommand { get; }
public Guid ItemId { get; set; }
public Guid GroupId { get; set; }
private string itemName = string.Empty;
public string ItemName
{
get { return itemName; }
set
{
if (value != null) itemName = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ItemName"));
}
}
private string itemDescription = string.Empty;
public string ItemDescription
{
get
{
return itemDescription.Trim();
}
set
{
if (value != null) itemDescription = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ItemDescription"));
}
}
public Item(string itemName, string itemDescription)
{
ItemName = itemName;
ItemDescription = itemDescription;
}
public Item()
{
LongPressItemCommand = new Command(() =>
{
MessagingCenter.Send<Item, Guid>(this, "PopupMenuItemMsg", ItemId);
});
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
You could check the code below with relative binding of Command.
Model:
public class Item
{
public string ItemName { get; set; }
public string ItemDescription { get; set; }
}
ViewModel:
public class ItemViewModel
{
public ICommand LongPressItemCommand { get; set; }
public Guid ItemId { get; set; }
public ObservableCollection<Item> Items { get; set; }
public ItemViewModel()
{
LongPressItemCommand = new Command(() =>
{
MessagingCenter.Send<ItemViewModel, Guid>(this, "PopupMenuItemMsg", ItemId);
});
CreateCollection();
}
public void LongPress()
{
}
public void CreateCollection()
{
Items = new ObservableCollection<Item>()
{
new Item(){ ItemName="A", ItemDescription="AA"},
new Item(){ ItemName="B", ItemDescription="BB"},
new Item(){ ItemName="C", ItemDescription="CC"},
};
}
}
Xaml:
<ContentPage.BindingContext>
<localvm:ItemViewModel></localvm:ItemViewModel>
</ContentPage.BindingContext>
<StackLayout>
<ListView ItemsSource="{Binding Items, Mode=TwoWay}" x:Name="lstView"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
SelectedItem="{Binding SelectedItem}" HasUnevenRows="True" RowHeight="50">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="0,0,8,0" Margin="4,0,4,0" xct:TouchEffect.LongPressCommand="{Binding Path=BindingContext.LongPressItemCommand, Source={x:Reference Name=lstView}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding ItemName}" TextColor="Black" Grid.Column="0" FontSize="Medium"></Label>
<Label Text="{Binding ItemDescription}" Grid.Column="1" VerticalTextAlignment="End"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Code behind:
MessagingCenter.Subscribe<ItemViewModel, Guid>(this, "PopupMenuItemMsg",
(page, itemId) =>
{
Main_PopupMenu(itemId);
});

Data Binding in Data Grid with collectionviewsource

I have an ObservableCollection of class
Public class Object
{
public string Name;
public Employee Employee;
}
public class Employee
{
// few properties
}
Here is my XMAL code for CollectionViewSource:
<CollectionViewSource x:Key="cvsTasks"
Source="{Binding Reels}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Name" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
Here is my DataGrid code:
<DataGrid ItemsSource="{Binding Source={StaticResource cvsTasks}}"/>
Now CollectionViewSource having PropertyGroupDescription on Name and I want to bind my DataGrid with Employee property of CollectionViewSource .
I dont know, if i understand your question correctly.
View:
<Window.Resources>
<CollectionViewSource x:Key="cvsTasks" Source="{Binding List}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Name" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid >
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Source={StaticResource cvsTasks}}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="EmpID" Binding="{Binding Employee.ID}"/>
<DataGridTextColumn Header="EmpDeportment" Binding="{Binding Employee.Department}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
ViewModel:
public class ViewModel {
public System.Collections.ObjectModel.ObservableCollection<Model.Root> List { get; set; }
public ViewModel() {
List = new System.Collections.ObjectModel.ObservableCollection<Model.Root> {
new Model.Root {
Name = "Peter",
Employee = new Model.Employee {
ID = 1,
Department = "IT"
}
},
new Model.Root {
Name = "Hans",
Employee = new Model.Employee {
ID = 2,
Department = "accounting"
}
},
new Model.Root {
Name = "Bilbo",
Employee = new Model.Employee {
ID = 3,
Department = "ceo"
}
}
};
}
}
Model:
public class Model {
public class Employee {
public int ID { get; set; }
public string Department { get; set; }
}
public class Root {
public string Name { get; set; }
public Employee Employee { get; set; }
}
}
And the result:

ListBox ItemTemplate - Display the property that the user selects at run time

I have a ListBox which is bound to a List of DataModel.
DataModel.cs
public class DataModel
{
public string Name { get; set; }
public string Desc { get; set; }
public string Code { get; set; }
}
The ListBox should display two properties, so I have defined the ItemTemplate as below
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBlock Text=" - "></TextBlock>
<TextBlock Text="{Binding Code}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
My requirement is that the user can select which two properties to display in the ListBox at run time. I am not sure how to achieve this. I have created a sample solution to explain my problem.
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"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Label Content="Property One"></Label>
<ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Label Content="Property Two"></Label>
<ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Button Content="Go" Click="ButtonBase_OnClick" Margin="3"></Button>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBlock Text=" - "></TextBlock>
<TextBlock Text="{Binding Code}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
}
}
public class ViewModel
{
public List<String> DataModelProperties { get; set; }
public List<DataModel> DataModelList { get; set; }
public ViewModel()
{
DataModelList = new List<DataModel>();
DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1" });
DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2" });
DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3" });
DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
}
}
public class DataModel
{
public string Name { get; set; }
public string Desc { get; set; }
public string Code { get; set; }
}
}
Things I have tried
<TextBlock Text="{Binding ElementName=ComboBox1, Path=SelectedItem}"></TextBlock>
This just displays the selected property name.
I suggest you to manage all the stuff regarding the "dynamic description" showed in the ListBox in your ViewModel.
First of all, your view model and your model should implement INotifyPropertyChanged. In my sample I created a simple base class which implements it. My base class is called NotifyPropertyChangedImpl.
Moreover I added two properties to the view model: the comboboxes for selecting the properties are binded to those two properties.
public class ViewModel : NotifyPropertyChangedImpl
{
private string property1;
private string property2;
public List<String> DataModelProperties { get; set; }
public List<DataModel> DataModelList { get; set; }
public string Property1
{
get
{
return property1;
}
set
{
if (property1 != value)
{
property1 = value;
SetDynamicDescriptions();
}
}
}
public string Property2
{
get
{
return property2;
}
set
{
if (property2 != value)
{
property2 = value;
SetDynamicDescriptions();
}
}
}
public ViewModel()
{
DataModelList = new List<DataModel>();
DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1" });
DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2" });
DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3" });
DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
}
private void SetDynamicDescriptions()
{
PropertyInfo propertyInfo1;
PropertyInfo propertyInfo2;
Type type = typeof(DataModel);
if (!String.IsNullOrEmpty(property1) && !String.IsNullOrEmpty(property2))
{
propertyInfo1 = type.GetProperty(property1);
propertyInfo2 = type.GetProperty(property2);
foreach (DataModel dataModel in DataModelList)
{
dataModel.DynamicDescription = String.Format("{0} - {1}",
propertyInfo1.GetValue(dataModel, null), propertyInfo2.GetValue(dataModel, null));
}
}
}
}
As you can see the method SetDynamicDescriptions rebuilds the DynamicsDescription each time Property1 or Property2 changes.
I also added a property to the model class:
public class DataModel : NotifyPropertyChangedImpl
{
private string dynamicDescription;
public string Name { get; set; }
public string Desc { get; set; }
public string Code { get; set; }
public string DynamicDescription
{
get
{
return dynamicDescription;
}
set
{
if (dynamicDescription != value)
{
dynamicDescription = value;
OnPropertyChanged("DynamicDescription");
}
}
}
}
So at the end your XAML will be:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Label Content="Property One"></Label>
<ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"
SelectedItem="{Binding Property1}"></ComboBox>
<Label Content="Property Two"></Label>
<ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"
SelectedItem="{Binding Property2}"></ComboBox>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DynamicDescription}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I hope this can help you.
In short, what I've done is I made 2 public properties to bind to and changed your properties to fields. Also I've made custom attribute, to control what fields user is able to select. And created a DisplayOptions class to store selection and spread it across DataModel instances.
The solution is a bit messy, considering it's a PoC, but I believe this can do:
XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="5">
<Label Content="Property One"></Label>
<ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Label Content="Property Two"></Label>
<ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
<Button Content="Go" Click="ButtonBase_OnClick" Margin="3"></Button>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding DisplayProperty1}"></TextBlock>
<TextBlock Text=" - "></TextBlock>
<TextBlock Text="{Binding DisplayProperty2}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
CS:
public partial class MainWindow : Window
{
public ViewModel model = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = model;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
//This part has to be on the View side, but this is PoC
model.Opts.Prop1 = typeof(DataModel).GetFields()
.Where(a => a.Name == ComboBox1.SelectedItem.ToString()).FirstOrDefault();
model.Opts.Prop2 = typeof(DataModel).GetFields()
.Where(a => a.Name == ComboBox2.SelectedItem.ToString()).FirstOrDefault();
DataContext = null;
DataContext = model;
}
}
public class ViewModel
{
public DisplayOptions Opts = new DisplayOptions();
public List<String> DataModelProperties { get; set; }
public List<DataModel> DataModelList { get; set; }
public ViewModel()
{
var properties = typeof(DataModel).GetFields()
.Where(a => a.CustomAttributes.Any(b => b.AttributeType == typeof(SwitchableAttr)));
//Initialising options before creating DataModel instances
DataModelProperties = properties.Select(s => s.Name).ToList();
Opts.Prop1 = properties.ElementAt(0);
Opts.Prop2 = properties.ElementAt(1);
DataModelList = new List<DataModel>();
DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1", options = Opts });
DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2", options = Opts });
DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3", options = Opts });
}
}
public class DisplayOptions
{
public FieldInfo Prop1;
public FieldInfo Prop2;
}
public class DataModel
{
public DisplayOptions options;
[SwitchableAttr]
public string Name;
[SwitchableAttr]
public string Desc;
[SwitchableAttr]
public string Code;
public string DisplayProperty1 { get { return (string)options.Prop1.GetValue(this); } set { } }
public string DisplayProperty2 { get { return (string)options.Prop2.GetValue(this); } set { } }
}
public class SwitchableAttr : Attribute { }
This is not Complete MVVM
so here is the code
public partial class MainWindow : Window
{
private ViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new ViewModel();
this.DataContext = vm;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(ComboBox1.Text) && !string.IsNullOrEmpty(ComboBox2.Text))
{
vm.AddDataToModel(ComboBox1.Text, ComboBox2.Text);
}
}
}
public class ViewModel
{
public List<String> DataModelProperties { get; set; }
private ObservableCollection<DataModel> _DataModelList;
public ViewModel()
{
_DataModelList = new ObservableCollection<DataModel>();
DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
}
public void AddDataToModel(string cmbx1Val,string cmbx2Val)
{
_DataModelList.Add(new DataModel() { Name = cmbx1Val, Code = cmbx2Val, Desc = "Desc1" });
}
public ObservableCollection<DataModel> DataModelList
{
get
{
return _DataModelList;
}
set
{
_DataModelList = value;
}
}
}

ObservableCollection with INotifyPropertyChanged into DatagridComboboxColumn Binding

My little project looks now quite different. Now I have ObservableCollection PackagesList with data which I want to bind with whole DataGrid (via Binding Path) and ObservableCollection DestinationItememsSource where I store cases for DataGridComboboxColumn (as ItemsSourceBinding). SelectedItem property in DatagridComboboxColumn is one value from PackageInfo (and it is one of DestinationNames cases ofc). Binding on DatagridTextBoxColumns is ok.
XAML:
<Window x:Class="test01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="400" Loaded="Window_Loaded">
<Grid>
<Border x:Name="mainBorder" Margin="20">
<DataGrid x:Name="mainDataGrid" x:Uid="mainDataGrid" AutoGenerateColumns="False"
AlternationCount="2" SelectionMode="Single" HorizontalAlignment="Stretch">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=id}"
Header="ID" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Path=name}"
Header="Name" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Path=quantity}"
Header="Quantity" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Path=price}"
Header="Price" Width="Auto" IsReadOnly="True"/>
<DataGridComboBoxColumn ItemsSource="{Binding DestinationItemsSource}"
SelectedItemBinding="{Binding Path=destination, UpdateSourceTrigger=PropertyChanged}"
Header="Destination" Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
</Border>
</Grid>
</Window>
C#:
public class PackageInfo
{
public int id { get; set; }
public string name { get; set; }
public int quantity { get; set; }
public double price { get; set; }
public string destination { get; set; }
}
public class DestinationNames : INotifyPropertyChanged
{
public string name { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public DestinationNames(string value)
{
this.name = value;
}
public string DestinationName
{
get { return name; }
set
{
name = value;
OnPropertyChanged("DestinationName");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public partial class MainWindow : Window
{
public ObservableCollection<DestinationNames> DestinationItememsSource { get; set; }
public ObservableCollection<PackageInfo> PackagesList { get; set; }
public MainWindow()
{
InitializeComponent();
}
public void Window_Loaded(object sender, RoutedEventArgs e)
{
LoadPackages();
}
public void LoadPackages()
{
DestinationItememsSource = new ObservableCollection<DestinationNames>();
DestinationItememsSource.Add(new DestinationNames("London"));
DestinationItememsSource.Add(new DestinationNames("Plymouth"));
DestinationItememsSource.Add(new DestinationNames("Birmingham"));
DestinationItememsSource.Add(new DestinationNames("Cambridge"));
PackagesList = new ObservableCollection<PackageInfo>();
PackagesList.Add(new PackageInfo { id = 1, name = "Potato", quantity = 3, price = 2.2, destination = "London" });
PackagesList.Add(new PackageInfo { id = 2, name = "Tomato", quantity = 5, price = 3.8, destination = "Plymouth" });
PackagesList.Add(new PackageInfo { id = 3, name = "Carrot", quantity = 1, price = 5.1, destination = "London" });
PackagesList.Add(new PackageInfo { id = 4, name = "Pea", quantity = 6, price = 1.8, destination = "Plymouth" });
PackagesList.Add(new PackageInfo { id = 5, name = "Bean", quantity = 2, price = 1.5, destination = "Birmingham" });
mainDataGrid.ItemsSource = PackagesList;
}
}
How to bind this DatagridComboboxColumn properly? Should I use INotifyCollectionChanged ?? What if I want to all data in datagrid will be automatically synced with ObservableCollection ?? Please help with some example.
Have PackageInfo implement INotifyPropertyChanged.
An ObservableCollection automatically implements INotifyCollectionChanged.
You will need to add List or ObservableCollection Destinations as a property of PackageInfo
NO class DestinationNames
Just a class DestinationName
Your binding to DestinationItememsSource does not work, because it isnt part of the PackageInfo object. You also cant bind to the Windows DataContext via FindAncestor or ElementName-binding because DataGrid-Columns wont be added to the visual tree.
One solution I can think of is using the CollectionViewSource:
<Window.Resources>
<CollectionViewSource Source="{Binding RelativeSource={RelativeSource Self}, Path=DestinationItememsSource}" x:Key="destinations">
<!--here you can also add Group and SortDescriptions-->
</CollectionViewSource>
</Window.Resources>
<DataGridComboBoxColumn ItemsSource="{Binding Source={StaticResource ResourceKey=destinations}}".../>
I cant test it atm, because I'm still downloading VS 2012.^^
Anotherone would be to simply add a List of Destinations to your PackageInfo object (but this would cause a lot of redundancies).

Creating treeview binding wpf

I have been trying to make a treeview that looks something like
2001(root)
-Student1(node)
-Student2(node)
I've tried to use hierarchicaldatatemplates but I'm still not grasping what I need to. This is my code that i'm looking to bind my treeview to. Any help with the Xaml would be appriciated.
I thought it would look something like
<TreeView ItemsSource="{Binding CurrentClass}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Student}" ItemsSource="{Binding CurrentClass.Students}">
<TextBlock Text="{Binding CurrentClass.Students/FirstName}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
public class ViewModel
{
public FreshmenClass currentClass = new FreshmenClass();
public ViewModel()
{
currentClass.Year = "2001";
currentClass.Students.Add(new Student("Student1", "LastName1"));
currentClass.Students.Add(new Student("Student2", "LastName2"));
}
public FreshmenClass CurrentClass
{
get { return currentClass; }
}
}
public class FreshmenClass
{
public string Year { get; set; }
public List<Student> students = new List<Student>();
public List<Student> Students
{
get { return students; }
set { students = value; }
}
}
public class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Student(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
Take a look to the documentation about Treeview and HierarchicalDataTemplate. Anyway I edit your example like this (XAML):
<TreeView ItemsSource="{Binding CurrentClass}" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Students}">
<TextBlock Text="{Binding Year}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding FirstName}"> </TextBlock>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
and c#:
public class ViewModel
{
private List<FreshmenClass> currentClass;
public ViewModel()
{
CurrentClass = new List<FreshmenClass>();
FreshmenClass temp = new FreshmenClass();
temp.Year = "2001";
temp.Students.Add(new Student("Student1", "LastName1"));
temp.Students.Add(new Student("Student2", "LastName2"));
CurrentClass.Add(temp);
}
public List<FreshmenClass> CurrentClass
{
get { return currentClass; }
set { currentClass = value; }
}
}
the ItemsSource property is an IEnumerable.

Categories

Resources