I'm trying to display a simple tree structure in WPF, and it's working but it shows all levels of nodes, at the top level of the tree. I have 9 nodes total in the structure, but only one should show at the root level. Under that should be two nodes, under those a couple, etc. Under each node, it does show the proper sub-nodes, but in addition it shows all nodes at the top. Here are my objects (these are EF db objects, if it matters):
public class ProductGroup
{
public int ProductGroupId { get; set; }
public ProductGroup Parent { get; set; }
public string Description { get; set; }
private ProductGroup() //not used, but needed to prevent EF error
{
Description = string.Empty;
}
public ProductGroup(string description)
{
Description = description;
}
public virtual ICollection<ProductGroup> Children { get; set; }
public void Add(ProductGroup newItem)
{
newItem.Parent = this;
if (Children == null)
Children = new Collection<ProductGroup>();
Children.Add(newItem);
}
public int Count
{
get { return Children.Count; }
}
}
And here is the xaml:
<Window
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:ConfiguratorDB="clr-namespace:ConfiguratorDB;assembly=ConfiguratorDB" mc:Ignorable="d" x:Class="ConfiguratorDBTest.Window2"
xmlns:local="clr-namespace:ConfiguratorDB;assembly=ConfiguratorDB"
Title="Window2" Height="417" Width="712" Loaded="Window_Loaded_1">
<Window.Resources>
<CollectionViewSource x:Key="productGroupViewSource" d:DesignSource="{d:DesignInstance {x:Type ConfiguratorDB:ProductGroup}, CreateList=True}"/>
</Window.Resources>
<Grid DataContext="{StaticResource productGroupViewSource}">
<DataGrid x:Name="productGroupDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding}" Margin="23,36,346,151" RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.Columns>
<DataGridTextColumn x:Name="descriptionColumn" Binding="{Binding Description}" Header="Description" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="productGroupIdColumn" Binding="{Binding ProductGroupId}" Header="Product Group Id" Width="SizeToHeader"/>
</DataGrid.Columns>
</DataGrid>
<TreeView x:Name="productGroupTreeView" ItemsSource="{Binding}" Margin="396,38,73,111">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:ProductGroup}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Path=Description}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
Here's what the output looks like. There shouldn't be any duplicates shown:
I was able to get the treeview to display correctly by filtering the CollectionViewSource based on whether the node was the root node. Basically you only want the root to be returned. The children will be handled by the hierarchical data template. Here are the lines of code I added to the codebehind of the window:
productGroupViewSource.Filter += productGroupViewSource_Filter;
void productGroupViewSource_Filter(object sender, FilterEventArgs e)
{
var node = (ProductGroup) e.Item;
e.Accepted = node.Parent == null;
}
Related
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
namespace MergeGridTest
{
/// <summary>
/// test1window.xaml 的交互逻辑
/// </summary>
public partial class test1window : Window
{
DrawingVisual dv = new DrawingVisual();
public test1window()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var Doctors = new List<Doctor>()
{
new Doctor(){Name="Zhang",Score=15,Address="Chengdu",Dept="Neike"},
new Doctor(){Name="Zhang",Score=18,Address="Chengdu",Dept="Neike"},
new Doctor(){Name="Zhang",Score=17,Address="Chengdu",Dept="Neike"},
new Doctor(){Name="Liu",Score=15,Address="Chengdu",Dept="Thke" },
new Doctor(){Name="Liu",Score=18,Address="MianYang",Dept="Thke"},
new Doctor(){Name="Liu",Score=17,Address="MianYang",Dept="Thke"}
};
TestGrid.ItemsSource = Doctors;
}
}
class Doctor
{
public string Name { get; set; }
public int Score { get; set; }
public string Address { get; set; }
public string Dept { get; set; }
}
}
<Window
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:MergeGridTest"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2" x:Class="MergeGridTest.test1window"
mc:Ignorable="d"
Title="test1window" Height="450" Width="800" Loaded="Window_Loaded">
<Grid>
<DataGrid Background="LightBlue" x:Name="TestGrid" AutoGenerateColumns="False" Margin="50" IsReadOnly="True"
CanUserAddRows="False" ItemsSource="{Binding}"
RowHeight="30" HorizontalGridLinesBrush="#FFCB0202" VerticalGridLinesBrush="{x:Null}" VerticalContentAlignment="Center">
<DataGrid.Columns>
<DataGridTextColumn Header="Address" Binding="{Binding Name}" Width="*"/>
<DataGridTextColumn Header="Score" Binding="{Binding Score}" Width="*"/>
<DataGridTextColumn Header="Address" Binding="{Binding Address}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
(https://note.youdao.com/yws/public/resource/ae451287f619092cc8b0485de1ddc297/xmlnote/AC1E5BD45A1C46A190271C395E0BBAA2/23392)
as show,I don't want to show the last line's (the last red line) under line in this data grid, but other horizontal line show normally, how to do it?
the data grid's line setting is for all lines, so I can't set the line visual style only the last line.
I am an experienced user of Winforms and I am now playing with WPF, but I am struggling a bit.
I have a datagrid with 2 columns.
The first column is a DataGridTextColumn which is bound to the "Name" property of my object.
The second column is bound to the "Power" property of my object.
When the user edits the Power column, I would like to display a combo box that lists all the Name of the first column in addition of "None" as first item.
How can I do that?
In addition, if the user updates any Name of the first column, I would like the change to be reflected in the Power column. Is it possible?
In code behind:
public partial class MainWindow : Window
{
ObservableCollection<MyObject> objects = new ObservableCollection<MyObject>();
public MainWindow()
{
InitializeComponent();
dgObjects.ItemsSource = objects;
}
}
public class MyObject
{
public String Name { get; set; }
public String Power { get; set; }
}
In Xaml:
<DataGrid Name="dgObjects" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridComboBoxColumn Header="Power" Binding="????"/>
</DataGrid.Columns>
</DataGrid>
Thanks
You can do this via binding on the DataContext. Though, first you need to set up the Window's DataContext properly. ItemsSource should not be done via code-behind (this is WPF, use binding!).
Set up your class structure like:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel
{
public ObservableCollection<MyObject> Objects { get; } = new ObservableCollection<MyObject>();
public ViewModel()
{
Objects.Add(new MyObject
{
Name = "Name"
});
Objects.Add(new MyObject
{
Name = "Name2"
});
Objects.Add(new MyObject
{
Name = "Name3"
});
}
}
public class MyObject
{
public String Name { get; set; }
public String Power { get; set; }
}
Now, update your xaml. You will need to use a CellTemplate and a standard Combobox. The ComboBox will then bind to the Objects collection, display the Name, but set the value in Power.
<Window x:Class="WpfApplication8.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"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
x:Name="MyWindow">
<Grid>
<DataGrid Name="dgObjects" AutoGenerateColumns="False"
ItemsSource="{Binding Objects}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ElementName=MyWindow, Path=DataContext.Objects}"
DisplayMemberPath="Name"
SelectedItem="{Binding Power}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
I have a view model with some fields, i.e.
type ViewModel =
member x.a = [1;2;3]
member x.b = [4;5;6]
member x.c = [7]
and in WPF application places some views, as:
<Control.Resources>
<DataTemplate x:Key="ItemTempl">
<TextBlock Text="{Binding}" />
</DataTemplate>
<DataTemplate x:Key="SomeTempl">
<ListBox ItemsSource="{Binding}"
ItemTemplate="{StaticResource ItemTempl}" />
</DataTemplate>
</Control.Resources>
<StackPanel>
<TabControl x:Name="ListBoxViewPresenter">
<TabItem Header="vm.a" Content="{Binding vm.a}"
ContentTemplate="{StaticResource SomeTempl}"/>
<TabItem Header="vm.b" Content="{Binding vm.b}"
ContentTemplate="{StaticResource SomeTempl}"/>
<TabItem Header="vm.c" Content="{Binding vm.c}"
ContentTemplate="{StaticResource SomeTempl}"/>
</TabControl>
<ListBox x:Name="ListBoxViewPresenter">
<ListBoxItem Content="{Binding vm.a}"
ContentTemplate="{StaticResource SomeTempl}" />
<ListBoxItem Content="{Binding vm.b}"
ContentTemplate="{StaticResource SomeTempl}" />
<ListBoxItem Content="{Binding vm.c}"
ContentTemplate="{StaticResource SomeTempl}" />
</ListBox>
</StackPanel>
What should I do to achieve such behavior:
when you clicked on some element in vm.a/b/c in ListBoxViewPresenter so same element in ListBoxViewPresenter is must selected in corresponding TabItem.
UPD:
Specifically my real problem, changing from origin topic.
I have ViewModel with fields: onelines, twolines... and a field with name selected_scheme.
In xaml:
<TreeViewItem Header="{Binding Path=name}" x:Name="ProjectArea">
<TreeViewItem Header="Однониточные планы" Style="{StaticResource MyTreeViewItem}">
<ContentPresenter Content="{Binding onelines}" ContentTemplate="{StaticResource SchemeWrapperTemplate}" />
</TreeViewItem>
<TreeViewItem Header="Двухниточные планы" Style="{StaticResource MyTreeViewItem}">
<ContentPresenter Content="{Binding twolines}" ContentTemplate="{StaticResource SchemeWrapperTemplate}" />
</TreeViewItem>
And data template:
<DataTemplate x:Key="SchemeWrapperTemplate">
<ListBox ItemsSource="{Binding schemes}"
ItemTemplate="{StaticResource SchemeTemplate}"
SelectedItem="{Binding selected_scheme}">
<ListBox.Style>
In other place of the program:
<Grid Grid.Row="1">
<TextBlock Text="{Binding selected_scheme.path}" />
</Grid>
And when you click on some ListBoxes then selected item not changing if you click on a yet SelectedItems.
First of all you should define the ItemsSource of your TabItem and ListBox in the ViewModel they are bound too.
Then you can bind their SelectedItem to a property in your ViewModel.
Here is a code sample (I was too lazy to create a seperate ViewModel class, hence it is mixed with my main window class, but you get the idea...) :
Codebehind :
namespace WpfApplication13
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private object _currentlySelectedItem;
public object CurrentlySelectedItem
{
get { return _currentlySelectedItem; }
set
{
_currentlySelectedItem = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("CurrentlySelectedItem"));
}
}
}
public class MyClass
{
public string MyString { get; set; }
public MyClass(string myString)
{
this.MyString = myString;
}
}
private List<MyClass> _myItemsSource = new List<MyClass>
{
new MyClass("toto"),
new MyClass("tata")
};
public List<MyClass> MyItemsSource
{
get { return _myItemsSource; }
set { _myItemsSource = value; }
}
public object A
{
get { return "toto"; }
}
public object B
{
get { return "tata"; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
}
}
xaml :
<Window x:Class="WpfApplication13.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>
<StackPanel>
<TabControl x:Name="ListBoxViewPresenter"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource}" />
<ListBox x:Name="ListBoxViewPresenter2"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource}" />
</StackPanel>
</Grid>
</Window>
New answer after you edited :
It does not make much sense to bind the same object to the SelectedItem property of two different lists that contain different elements. You have to redesign your application or you may be confronted to many problems due to this strange design in the futur.
Still, you can achieve want you want to do with a little codebehing. When the user clicks on one of your ListBox, you set the selected item of your other listbox to null. Then you will be notified when you re-click on the first listbox since the selection goes from null to something.
xaml:
<Window x:Class="WpfApplication13.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>
<StackPanel>
<ListBox x:Name="ListBoxViewPresenter"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource}" />
<ListBox x:Name="ListBoxViewPresenter2"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource2}" />
</StackPanel>
</Grid>
</Window>
codebehind:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using System.ComponentModel;
namespace WpfApplication13
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private object _currentlySelectedItem;
public object CurrentlySelectedItem
{
get { return _currentlySelectedItem; }
set
{
_currentlySelectedItem = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("CurrentlySelectedItem"));
}
}
}
private List<int> _myItemsSource = new List<int> { 1, 2 };
private List<int> _myItemsSource2 = new List<int> { 3, 4 };
public List<int> MyItemsSource
{
get { return _myItemsSource; }
set { _myItemsSource = value; }
}
public List<int> MyItemsSource2
{
get { return _myItemsSource2; }
set { _myItemsSource2 = value; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
ListBoxViewPresenter.PreviewMouseDown += ListBoxViewPresenter_PreviewMouseDown;
ListBoxViewPresenter2.PreviewMouseDown += ListBoxViewPresenter2_PreviewMouseDown;
}
void ListBoxViewPresenter_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ListBoxViewPresenter2.SelectedItem = null;
}
void ListBoxViewPresenter2_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ListBoxViewPresenter.SelectedItem = null;
}
}
}
I have a window that uses a viewmodel. This screen contains 2 listviews on a screen. The first listview binds to a propery on my viewmodel called projects. This property returns a model as follows
class ProjectsModel
{
public string ProjectName { get; set; }
public ObservableCollection<ProjectModel> ProjectDetails { get; set; }
}
In this class the ProjectModel looks like the following
public class ProjectModel
{
public string ProjectName { get; set; }
public string ProjectId { get; set; }
public string ProjectFileId { get; set; }
public string ProjectSource { get; set; }
public string ClientCode { get; set; }
public string JobNumber { get; set; }
}
The first listview shows projectname as i expect but I would like it so that when I click on any of the items, the second listview should display its details of the projectdetails property. It almost appears to work has it shows the first items childrean but I beleive that its not being informed that the selected item of the first listview has changed. Ho can I do this? Any ideas would be appreciated becuase Ive been pulling my hair out for hours now!
This is the xaml
<Window x:Class="TranslationProjectBrowser.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TranslationProjectBrowser.ViewModels"
xmlns:local="clr-namespace:TranslationProjectBrowser.Models"
Title="MainWindow" Height="373" Width="452" Background="LightGray">
<Window.DataContext>
<vm:ProjectBrowserViewModel></vm:ProjectBrowserViewModel>
</Window.DataContext>
<Window.Resources>
<ObjectDataProvider x:Key="projectList" ObjectType="{x:Type vm:ProjectBrowserViewModel}" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource projectList}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="176*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="176*" />
<RowDefinition Height="254*" />
</Grid.RowDefinitions>
<DockPanel LastChildFill="True" >
<TextBlock DockPanel.Dock="Top" Text="Projects" Margin="5,2"></TextBlock>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<TextBox Name="ProjectName" Width="140" Margin="5,2" Height="18" FontFamily="Calibri" FontSize="10"></TextBox>
<Button Height="18" Width="45" HorizontalAlignment="Left" Margin="0,2" FontSize="10" Content="Add" Command="{Binding Path=AddProject}" CommandParameter="{Binding ElementName=ProjectName, Path=Text}"></Button>
<TextBlock Text="{Binding Path=ErrorText}" VerticalAlignment="Center" Margin="6,2" Foreground="DarkRed"></TextBlock>
</StackPanel>
<ListView Name="project" HorizontalAlignment="Stretch" Margin="2" ItemsSource="{Binding Path=Projects}" IsSynchronizedWithCurrentItem="True">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Path=ProjectName}" Header="Name" Width="200" />
</GridView>
</ListView.View>
</ListView>
</DockPanel>
<DockPanel LastChildFill="True" Grid.Row="1" >
<TextBlock DockPanel.Dock="Top" Text="Project Files" Margin="5,2"></TextBlock>
<ListView HorizontalAlignment="Stretch" Margin="2" ItemsSource="{Binding Path=Projects/ProjectDetails}" IsSynchronizedWithCurrentItem="True" >
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=ProjectName}" Width="200" />
<GridViewColumn Header="Job Number" DisplayMemberBinding="{Binding Path=JobNumber}" Width="100" />
</GridView>
</ListView.View>
</ListView>
</DockPanel>
</Grid>
Your view models should (at least) implement INotifyPropertyChanged. This is how WPF will know when your selction (or other properties) change and the binding needs to be updated.
So you should have something like this:
class ProjectsModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public string ProjectName
{
get
{
return _projectName;
}
set
{
_projectName = value;
NotifyPropertyChanged("ProjectName");
}
}
public ObservableCollection<ProjectModel> ProjectDetails
{
get
{
return _projectDetails;
}
set
{
_projectDetails = value;
NotifyPropertyChanged("ProjectDetails");
}
}
}
In future versions of the .NET framework this gets a lot easier with the "caller info" attributes (http://www.thomaslevesque.com/2012/06/13/using-c-5-caller-info-attributes-when-targeting-earlier-versions-of-the-net-framework/). But as of today this is usually how it's done.
UPDATE
Ok, so based on your comment you need to bind your ListView's SelectedItem property to a property on your view model. You can then Bind your second ListView to that property as well. Something like this:
<ListView ... SelectedItem="{Binding Path=FirstListViewSelectedItem, Mode=TwoWay}" .. >
And then your second list view would be sometihng like this:
<ListView ... ItemsSource="{Binding Path=FirstListViewSelectedItem.ProjectDetails, Mode=OneWay" .. />
I don't see any current management in your code. If you use a CollectionView you will get that for free, see below sample:
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">
<StackPanel>
<ListBox ItemsSource="{Binding Path=ProjectsView}" DisplayMemberPath="Name" IsSynchronizedWithCurrentItem="True"/>
<ListBox ItemsSource="{Binding Path=ProjectsView/ProjectDetails}" />
</StackPanel>
</Window>
Code behind:
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new VM();
}
}
public class VM
{
public VM()
{
List<Project> projectsModel = new List<Project>();
projectsModel.Add(new Project("AAA"));
projectsModel.Add(new Project("BBB"));
projectsModel.Add(new Project("CCC"));
ProjectsView = CollectionViewSource.GetDefaultView(projectsModel);
}
public ICollectionView ProjectsView { get; private set; }
}
public class Project
{
public Project(string name)
{
Name = name;
}
public string Name { get; private set; }
public IEnumerable<string> ProjectDetails
{
get
{
for (int i = 0; i < 3; i++)
{
yield return string.Format("{0}{1}", Name, i);
}
}
}
}
}
I am getting an InvalidOperationException('DeferRefresh' is not allowed during an AddNew or EditItem transaction.) from my datagrid when I try to edit the value of a combo box column. The items I am showing all have a reference to one other item in the same list so this is what I am using the combobox for. It is bound to the same collection as the datagrid is. My application I am working on is targetting .NET 3.5, but I have put together an example that is exactly the same in .NET 4 since the datagrid is built in. Here is the code for items in the datagrid:
public class TestItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private int m_ID;
private string m_Name;
private int m_OppositeID;
public int ID
{
get { return m_ID; }
set
{
m_ID = value;
RaisePropertyChanged("ID");
}
}
public string Name
{
get { return m_Name; }
set
{
m_Name = value;
RaisePropertyChanged("Name");
}
}
public int OppositeID
{
get { return m_OppositeID; }
set
{
m_OppositeID = value;
RaisePropertyChanged("OppositeID");
}
}
public TestItem(int id, string name, int oppID)
{
ID = id;
Name = name;
OppositeID = oppID;
}
}
This is the code in my window:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private ObservableCollection<TestItem> m_Items;
public ObservableCollection<TestItem> Items
{
get { return m_Items; }
set
{
m_Items = value;
RaisePropertyChanged("Items");
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Items = new ObservableCollection<TestItem>();
Items.Add(new TestItem(0, "Fixed", 0));
Items.Add(new TestItem(1, "Left Side", 2));
Items.Add(new TestItem(2, "Right Side", 1));
}
}
and finally my xaml:
<Window x:Class="DataGrid_Combo_Test.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>
<DataGrid ItemsSource="{Binding Path=Items}" AutoGenerateColumns="False">
<DataGrid.Resources>
<Style x:Key="ItemsSourceStyle" TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding Path=DataContext.Items, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=ID}" Header="ID" Width="*"/>
<DataGridTextColumn Binding="{Binding Path=Name}" Header="Name" Width="*"/>
<DataGridComboBoxColumn Header="Opposite Item" Width="*" DisplayMemberPath="Name" SelectedValuePath="ID" SelectedValueBinding="{Binding Path=OppositeID}" ElementStyle="{StaticResource ItemsSourceStyle}" EditingElementStyle="{StaticResource ItemsSourceStyle}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Thanks in advance for any assistance you can offer!
I found out how to fix this issue.
I created a CollectionViewSource like this in my Window.Resources:
<Window.Resources>
<CollectionViewSource x:Key="itemSource" Source="{Binding Path=Items}"/>
</Window.Resources>
Then changed my combobox column definition to the following:
<DataGridComboBoxColumn Header="Opposite Item" Width="*" DisplayMemberPath="Name" SelectedValuePath="ID" SelectedValueBinding="{Binding Path=OppositeID}" ItemsSource="{Binding Source={StaticResource itemSource}}"/>
Try following sequence before calling Refersh on CollectionView or DataGridXYZ.Items
DataGridX.CommitEdit();
DataGridX.CancelEdit();
Worked for me.