I'm trying to populate a DataGrid by deleting items from another Datagrid that got populated from the database.
Removing them from the original one seems to work flawlessly but AllItems.Remove(allItem) seems to be causing problems.
Whenever I click on Add on the left grid, the right one does get populated by one. When I Try it for a second time it doesn't do anything and the third time it crashes with this line.
Unhandled Exception: System.InvalidOperationException: An ItemsControl
is inconsistent with its items source. See the inner exception for
more information. ---> System.Exception: Information for developers
(use Text Visualizer to read this): This exception was thrown because
the generator for control 'System.Windows.Controls.DataGrid
Items.Count:3' with name 'AddedItemsGrid' has received sequence of
CollectionChanged events that do not agree with the current state of
the Items collection. The following differences were detected:
Accumulated count 2 is different from actual count 3. [Accumulated
count is (Count at last Reset + #Adds - #Removes since last Reset).]
If you don't exactly understand what I'm trying here's a brief explanation :
LeftGrid gets populated by database
RightGrid gets populated by clicking on the add button on the LeftGrid whilst the items also get removed from the RightGrid
Clicking on Finish sends the RightGrid collection to the database
View
<UserControl x:Class="VivesRental.GUI.View.NewRentalView"
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:VivesRental.GUI.View"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.15*" />
<RowDefinition Height="0.75*" />
<RowDefinition Height="0.1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Margin="10px">
<Run FontWeight="Bold" FontSize="22" Foreground="Black" Text="New Rental for" />
<Run FontWeight="Bold" FontSize="22" Foreground="Black" Text="{Binding User.Name}" />
</TextBlock>
<Button Grid.ColumnSpan="2" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Top" Content="Close" Command="{Binding CloseCommand}" Margin="3"></Button>
<DataGrid x:Name="AllItemsGrid" ItemsSource="{Binding AllItems}" CanUserAddRows="False" CanUserDeleteRows="False" AutoGenerateColumns="False" CanUserResizeRows="False" SelectionMode="Single" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="False" Grid.Row="1" Grid.Column="0">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Item.Name}"></DataGridTextColumn>
<DataGridTemplateColumn Width="0.2*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Add" Command="{Binding DataContext.AddItemCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" CommandParameter="{Binding Id}" Click="OnAddButton" ></Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<DataGrid x:Name="AddedItemsGrid" ItemsSource="{Binding AddedItems}" CanUserAddRows="False" CanUserDeleteRows="False" AutoGenerateColumns="False" CanUserResizeRows="False" SelectionMode="Single" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="False" Grid.Row="1" Grid.Column="1">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Item.Name}"></DataGridTextColumn>
<DataGridTemplateColumn Width="0.2*">
<DataGridTemplateColumn.CellTemplate >
<DataTemplate>
<Button Content="Remove" Command="{Binding DataContext.RemoveItemCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" CommandParameter="{Binding Id}"></Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Grid.Row="2" Grid.ColumnSpan="2" Content="Finish" Command="{Binding FinishCommand}" Margin="3"></Button>
</Grid>
</UserControl>
ViewModel
public class NewRentalViewModel : ViewModelBase, IViewModel
{
private ItemService itemService;
private UserService userService;
private RentalItemService rentalItemService;
private ICollection<RentalItem> allItems;
private ICollection<RentalItem> addedItems;
private User user;
public RelayCommand CloseCommand { get; private set; }
public RelayCommand<int> AddItemCommand { get; private set; }
public RelayCommand<int> RemoveItemCommand { get; private set; }
public ICollection<RentalItem> AllItems
{
get { return allItems; }
set
{
allItems = value;
RaisePropertyChanged();
}
}
public ICollection<RentalItem> AddedItems
{
get { return addedItems; }
set
{
addedItems = value;
RaisePropertyChanged();
}
}
public User User
{
get { return user; }
set
{
user = value;
RaisePropertyChanged();
}
}
public NewRentalViewModel(int userId)
{
userService = new UserService();
itemService = new ItemService();
User = userService.Get(userId);
rentalItemService = new RentalItemService();
InstantiateCommands();
LoadItems();
}
private void InstantiateCommands()
{
CloseCommand = new RelayCommand(Close);
AddItemCommand = new RelayCommand<int>(AddItem);
RemoveItemCommand = new RelayCommand<int>(RemoveItem);
}
private void LoadItems()
{
AllItems = rentalItemService.GetAvailableRentalItems(new RentalItemIncludes(){Item = true});
AddedItems = new List<RentalItem>();
}
private void Close()
{
var viewModel = new NavigationViewModel();
var message = new NavigationMessage { ViewModel = viewModel };
Messenger.Default.Send(message);
}
private void AddItem(int itemId)
{
foreach (var allItem in AllItems)
{
if (allItem.Id == itemId)
{
AllItems.Remove(allItem);
AddedItems.Add(allItem);
break;
}
}
}
private void RemoveItem(int itemId)
{
}
}
View.cs
public partial class NewRentalView : UserControl
{
public NewRentalView()
{
InitializeComponent();
}
private void OnAddButton(object sender, RoutedEventArgs e)
{
//Item item = (Item)((Button)sender).Tag;
//AllItemsGrid.Items.RemoveAt(item.Id);
AllItemsGrid.Items.Refresh();
AddedItemsGrid.Items.Refresh();
}
}
It would help if this was an MCVE. For example, I need to assume that your ICollection<T>s are ObservableCollections. Since it's not, it means I can't repeat your problem and will have to guess.
The indication from the error is that WPF is struggling to keep up with the changes being made to the observable collection. This doesn't normally happen.
In the given AddItem relay command, it iterates through the AllItems collection. In the middle of the iteration, it Removes an item from the collection, albeit that it breaks out of the foreach immediately after. This approach can be avoided by using Linq's First method:
private void AddItem(int itemId)
{
var allItem = AllItems.First(i=>i.Id == itemId);
AllItems.Remove(allItem);
AddedItems.Add(allItem);
}
But you can actually go a step further than that and make life even easier for yourself. In the XAML change the CommandParameter="{Binding Id}" to be CommandParameter="{Binding}", and change the parameter of AddItem to be a RentalItem rather than an int (also change the declaration of the RelayCommand). This way, you don't need to find the item because it is being passed as the parameter.
I'm hoping that doing this will mean the the changes to the collection can be processed in a timely fashion.
Related
in my CustomerListViewModel i definded a 'delegate' event that needs to call a method in my mainViewModel and pass the GUID of my Customer to the method defined in this vm. only the delegate is not working correctly and the event is never passed to my mainViewModel :(
public partial class CustomerListViewModel : ObservableObject
{
public CustomerListViewModel()
{
customerCollection = new ObservableCollection<Customer>();
Customer jacob = new Customer("Jacob", "Woord", "NederLand");
Customer Maria = new Customer("Maria", "Woord", "Nederland");
customerCollection.Add(jacob);
customerCollection.Add(Maria);
}
public event Action<Guid> PlaceOrderRequested = delegate { };
[RelayCommand]
void PlaceOrder(Customer obj)
{
PlaceOrderRequested(obj.CustomerId);
}
[ObservableProperty]
ObservableCollection<Customer> customerCollection;
// Can Execute Example (if there no item selected in the list the button get disbled
public bool canDelete => selectedCustomer != null;
[RelayCommand(CanExecute =nameof(canDelete))]
void Delete()
{
customerCollection.Remove(selectedCustomer);
}
//Notifys the change for the selected Customer
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(DeleteCommand))]
Customer selectedCustomer;
[RelayCommand]
void DeleteAll()
{
customerCollection.Clear();
}
namespace viewModelBinding
{
public partial class MainWIndowViewModel : ObservableObject
{
//intialize every vieModel
private CustomerListViewModel customerListViewModel = new CustomerListViewModel();
private MaterialListViewModel materialListViewModel = new MaterialListViewModel();
private OrderListViewModel orderListViewModel = new OrderListViewModel();
private OrderPrepViewModel orderPrepViewModel = new OrderPrepViewModel();
private Timer timer = new Timer(5000);
public MainWIndowViewModel()
{
customerListViewModel.PlaceOrderRequested += CustomerListViewModel_PlaceOrderRequested;
currentViewModel = new OrderListViewModel();
timer.Elapsed += (s, e) => NotificationMessage = "Na 5 seconden word de huidige tijd neergzet: " + DateTime.Now.ToLocalTime() + " Kukulukuu";
timer.Start();
}
private void CustomerListViewModel_PlaceOrderRequested(Guid obj)
{
orderListViewModel.CustomerId = obj;
currentViewModel = orderListViewModel;
}
[ObservableProperty]
object currentViewModel;
[ObservableProperty]
string notificationMessage;
//switch statement with a parameter that gets a command parameter from the button as a string
[RelayCommand]
void OnNav(string destination)
{
switch (destination)
{
case "orders":
CurrentViewModel = orderListViewModel;
break;
case "customers":
CurrentViewModel = customerListViewModel;
break;
default:
CurrentViewModel = materialListViewModel;
break;
}
}
}
}
And below my vie where i defined the binding
<UserControl x:Class="viewModelBinding.Customers.CustomerListView"
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:local="clr-namespace:viewModelBinding.Customers"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.InputBindings>
<KeyBinding Key="D" Modifiers="Control" Command="{Binding DeleteCommand}"/>
</UserControl.InputBindings>
<UserControl.Resources>
<DataTemplate x:Key="CustomerListTemplate">
<Border BorderThickness="2"
BorderBrush="red">
<StackPanel>
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding LastName}"/>
<TextBlock Text="{Binding CityCustomer}"/>
</StackPanel>
</Border>
</DataTemplate>
</UserControl.Resources>
<UserControl.DataContext>
<local:CustomerListViewModel/>
</UserControl.DataContext>
<!--<b:Interaction.Triggers>
<b:EventTrigger EventName="">
<b:CallMethodAction TargetObject="{Binding}" MethodName="{Binding DeleteAllCommand}"/>
</b:EventTrigger>
</b:Interaction.Triggers>-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0"
Content="Delete"
HorizontalAlignment="Left"
Width="75"
Command="{Binding DeleteCommand}"/>
<Button Grid.Row="0"
Content="empty"
HorizontalAlignment="Right"
Width="75"
/>
<DataGrid ItemsSource="{Binding CustomerCollection}"
Grid.Row="1"
x:Name="CustomerList"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn x:Name="nameColumn"
Binding="{Binding FirstName}"
Width="*"/>
<DataGridTemplateColumn Width="auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!--with this command we grap the dataContext of the right customer to look at the anchestor (DataGrid)-->
<Button Content="Place Order"
Command="{Binding DataContext.PlaceOrderCommand,RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}"
Margin="5"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding CustomerId}" Width="auto">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<ListBox SelectedItem="{Binding SelectedCustomer}"
ItemsSource="{Binding CustomerCollection}"
Grid.Row="2" ItemTemplate="{StaticResource CustomerListTemplate}"/>
</Grid>
</UserControl>
Your problem is because you have two instances of CustomerListViewModel
This is one instance. ( Remove it.)
<UserControl.DataContext>
<local:CustomerListViewModel/>
</UserControl.DataContext>
This is another.
private CustomerListViewModel customerListViewModel = new CustomerListViewModel();
But the delegate approach is a bad idea.
I recommend messenger instead of a delegate.
Rather than pass a hard reference you could keep the two viewmodels decoupled.
https://learn.microsoft.com/en-us/windows/communitytoolkit/mvvm/messenger
// Create a message
public class LoggedInUserChangedMessage : ValueChangedMessage
{
public LoggedInUserChangedMessage(User user) : base(user)
{
}
}
// Register a message in some module
WeakReferenceMessenger.Default.Register<LoggedInUserChangedMessage>(this,
(r, m) =>
{
// Handle the message here, with r being the recipient and m being the
// input message. Using the recipient passed as input makes it so that
// the lambda expression doesn't capture "this", improving performance.
});
// Send a message from some other module
WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(user));
Obviously, here you want to pass a guid in the object you send. You could elsewhere pass a type or even an entire viewmodel for navigation purposes.
It seems odd that you're passing a guid back to mainwindowviewmodel.
I would have thought there'd be views for orders and new order. New order would let you pick a customer so would have some sub view where you look a customer up. The command to pick a customer would be in new order viewmodel and bound from customer view so the command passed customer id.
Maybe this is just purely experimental code though.
So here I have a MVVM form. the Form contains a Datagrid which is connected to the Databank. I also have a ComboBox which I want to use as a filter option. The Filter option shoud filter by the "AnlV nr" so when the user selects "01" from the ComboBox the datagrid should refresh and show only that "AnlV nr" that have "01" Below I will share you the code and you can see that i've gotten as far as showing the "AnlV" values in the ComboBox but I now do not know how to do the rest and make the filter work. Below is my Viewmodel and the Xaml code.
If anyone can help me with this I would really apreciate it.
Xaml Code:
<Window x:Class="QBondsFrontend.Views.Input.AnlVTexteView"
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:QBondsFrontend.Views.Input" xmlns:input="clr-namespace:QBondsFrontend.ViewModels.Input" d:DataContext="{d:DesignInstance Type=input:AnlVTexteViewModel}"
mc:Ignorable="d"
Title="AnlVTexteView"
Width="800"
MinHeight="400"
Height="490"
MinWidth="1010"
MaxWidth="1010"
UseLayoutRounding="True">
<Grid Background="#A8A8A8" >
<StackPanel VerticalAlignment="Top" Background="#A8A8A8" Orientation="Horizontal" Height="57">
<Label
Content="AnlV Nr.:" Height="35" FontSize="12"/>
<ComboBox Background="LightGray" Height="20" Width="70" ItemsSource="{Binding lstAnlVTexte}" SelectedItem="{Binding Search}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding AnlVPara}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Height="18" Width="68" Margin="5, 0"
Content="Filter löschen" FontSize="11" Style="{StaticResource STL_ButtonStandard}"
x:Name="filterlöschen"
Command="{Binding icdFilterDelete}"/>
</StackPanel>
<StackPanel Background="LightGray" VerticalAlignment="Top" Height="177" Margin="0,57,0,0">
<DataGrid x:Name="datagridXAML"
Height="177"
ItemsSource="{Binding lstAnlVTexte, Mode=TwoWay}"
Style="{StaticResource STL_DataGridReporting}"
CellStyle="{StaticResource STL_DataGridCellReporting}"
ColumnHeaderStyle="{StaticResource STL_DataGridColumnHeaderReporting}"
AlternatingRowBackground="#A8A8A8"
CanUserResizeColumns="False"
>
<DataGrid.Columns>
<DataGridTextColumn Header="AnlV-Nr"
Binding="{Binding AnlVPara}"
Width="60"/>
<DataGridTextColumn Header="gültig ab"
Binding="{Binding TextGueltigAb}"
Width="68"/>
<DataGridTextColumn Header="Text"
Binding="{Binding ParaText}"
Width="750"/>
<DataGridTextColumn Header="Info"
Binding="{Binding Info}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<StackPanel Background="#A8A8A8" HorizontalAlignment="Center" Margin="10,268,0,141" Width="1010" >
<Label Content="Bearbeitungsbereich" FontWeight="Bold" FontSize="12" Height="33" />
</StackPanel>
<StackPanel>
<StackPanel Orientation="Horizontal" Background="#A8A8A8" HorizontalAlignment="Center"
Width="1010" Margin="0,294,0,0" Height="31">
<Label Height="25" Width="60" Margin="20, 0, 0, 0" Content="AnlV-Nr.:" />
<ComboBox IsEditable="True" Background="gray" Height="22" Width="69" ItemsSource="{Binding AnlVPara}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding lstAnlVTexte}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<CheckBox Height="15" Margin="10, 0, 0, 0" />
<Label Height="26" Width="122" Content="Editierwarnungen" />
<StackPanel Height="48" Width="100"/>
</StackPanel>
<StackPanel Height="22" Orientation="Horizontal">
<Label Margin="20, 0, 0, 0" Content="gültig ab:" Height="27" />
<TextBox Background="LightGray" Height="20" Width="100" />
</StackPanel>
<StackPanel Height="50" Orientation="Horizontal">
<Label Content="Text:" Height="27" Width="38" Margin="42,0,0,10" />
<TextBox Background="LightGray" Width="500" Height="43" />
</StackPanel>
<StackPanel Orientation="Horizontal" >
<Label Content="Info:" Height="27" Width="38" Margin="42,0,0,0" />
<TextBox Background="LightGray" Width="500" Height="20" />
<Button x:Name="BTN_speichern" Width="80" Height="18" Margin="20,0,0,0" Content="Speichern"
Style="{StaticResource STL_ButtonStandard}" Command="{Binding icdSpeichern}"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
ViewModel:
using Newtonsoft.Json;
using QBondsData.DBModels;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Linq;
namespace QBondsFrontend.ViewModels.Input
{
public class AnlVTexteViewModel : BaseViewModel
{
#region data
private string _AnlVPara;
private DateTime _TextGueltigAb;
private string _ParaText;
private string _Info;
private List<AnlVhistText> _lstAnlVTexte;
private string _search;
#endregion
#region constructor
public AnlVTexteViewModel()
{
icdFilterDelete = new RelayCommand<object>(parameter => filterdelete(), parameter => true);
icdSpeichern = new RelayCommand<object>(parameter => speichern(), parameter => true);
GetData();
}
#endregion
#region members
public ICommand icdFilterDelete { get; set; }
public ICommand icdSpeichern { get; set; }
private string Search
{
get { return _search; }
set
{
_search = value;
OnPropertyChanged("Search");
}
}
public string AnlVPara
{
get
{
return _AnlVPara;
}
set
{
_AnlVPara = value;
OnPropertyChanged("AnlVPara");
}
}
public DateTime TextGueltigAb
{
get
{
return _TextGueltigAb;
}
set
{
_TextGueltigAb = value;
OnPropertyChanged("TextGueltigAb");
}
}
public string ParaText
{
get
{
return _ParaText;
}
set
{
_ParaText = value;
OnPropertyChanged("ParaText");
}
}
public string Info
{
get
{
return _Info;
}
set
{
_Info = value;
OnPropertyChanged("Info");
}
}
public List<AnlVhistText> lstAnlVTexte
{
get { return _lstAnlVTexte; }
set
{
_lstAnlVTexte = value;
OnPropertyChanged("lstAnlVTexte");
}
}
#endregion
#region methods
private void filterdelete()
{
}
private void speichern()
{
}
private async Task GetData()
{
HttpResponseMessage Response = await Globals.SendRequest("AnlVTexte/GetAnlVTexte"
, "GET");
if (Response.IsSuccessStatusCode)
{
lstAnlVTexte = JsonConvert.DeserializeObject<List<AnlVhistText>>
(await Response.Content.ReadAsStringAsync());
}
else
{
lstAnlVTexte = new List<AnlVhistText>();
Application.Current.Dispatcher.Invoke((Action)delegate
{
Globals.CloseReportByHash(this.GetHashCode()
, "Fehler! (HTTP Status " + Response.StatusCode + ")." +
"Kontaktieren Sie den Support.");
});
}
}
#endregion
}
}
When you change the type of lstAnlVTexte to ICollectionView you get two events CurrentChanged and CurrentChanging which you can handle in your viewmodel. From the ICollectionView you can get the CurrentItem.
Like this:
private List<AnlVhistText> _anlVTexte = new List<AnlVhistText>();
public AnlVTexteViewModel()
{
[...]
lstAnlVTexte = new CollectionView(_anlVTexte);
lstAnlVTexte.CurrentChanged += SelectionChanged; // raised after the current item has been changed.
lstAnlVTexte.CurrentChanging += SelectionChanging; // raise before changing the current item. Event handler can cancel this event.
}
private void SelectionChanged(object sender, EventArgs e)
{
var selectedItem = lstAnlVTexte.CurrentItem;
}
private void SelectionChanging(object sender, CurrentChangingEventArgs e)
{
}
private ICollectionView _lstAnlVTexte;
public ICollectionView lstAnlVTexte
{
get { return _lstAnlVTexte; }
set
{
_lstAnlVTexte = value;
OnPropertyChanged("lstAnlVTexte");
}
}
Here's a sample using the community toolkit mvvm and linq.
If you're not familiar, the toolkit does code generation.
This is a simple scenario to illustrate the approach.
Mainwindowviewmodel.
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private int selectedFilterInt = -1;
partial void OnSelectedFilterIntChanged(int newValue)
{
FilteredList = new ObservableCollection<MyItem>(FullList.Where(x=>x.Category == selectedFilterInt).ToList());
}
public List<int> FilterOptions { get; set; } = new List<int> {1,2,3};
private List<MyItem> FullList= new List<MyItem>
{
new MyItem{ Category = 1},
new MyItem{ Category = 1},
new MyItem { Category = 1 },
new MyItem { Category = 2 },
new MyItem { Category = 2 },
new MyItem { Category = 3 }
};
[ObservableProperty]
private ObservableCollection<MyItem> filteredList = new ObservableCollection<MyItem>();
public MainWindowViewModel()
{
FilteredList = new ObservableCollection<MyItem>(FullList);
}
}
There's a full list of all the items.
But a filtered list is going to be bound to the itemssource of my listbox ( equivalent to your datagrid ).
Due to the code generated, when selectedFilterInt changes, OnSelectedFilterIntChanged will be called. It's got a handler listening for property changed of SelectedFilterInt if you dug into the generated code.
That method uses a linq where to filter the full list into filtered list.
Setting that filtered list property raises property changed and the view re reads the new collection.
MainWindow. ( I did mention this is simplified )
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<ComboBox SelectedItem="{Binding SelectedFilterInt}"
ItemsSource="{Binding FilterOptions}"/>
<ListBox ItemsSource="{Binding FilteredList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Category}"/>
<TextBlock Text="{Binding Comment}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
and MyItem
public partial class MyItem : ObservableObject
{
[ObservableProperty]
private int category = 0;
[ObservableProperty]
private string comment = "Some test string";
}
Which is a bit underwhelming visually but works:
In your code you need to get all the data into a collection.
Call that FulList.
You then need another collection which will be the filtered data.
Call this FilteredList.
Bind itemssource to FilteredList
Initially, you presumably want FilteredList to be = FullList
Then when the user selects something in the combobox you need to react to that.
You could bind selecteditem to a property and act in the setter or handle propertychanged like my code does.
However you do it, you get a new integer.
You then use that to filter FullList into a new collection which will replace the bound FilteredList.
You also need to somehow have one entry per AnlV nr whatever that is in your combobox.
AnlV nr isn't going to work as a property name since it's got a space but it is the equivalent to Category in my sample.
You will use that selected value in the linq.
Substitute the name of that property for Category. Substitute the type of whatever your collection is. Maybe that's AnlVhistText. I'm not sure.
I've a ListView whose data I select and send to a DataGrid. I am having trouble with the quantity column of the DataGrid which I would want to calculate how many times a ListView item has been added to the said DataGrid (I'm currently displaying a success message when the same item is selected). I would also want to calculate the price and the quantity and display them on one column named 'price' the DataGrid.
Here is the Datagrid
<ListView x:Name="ItemGridView" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" PreviewMouseDoubleClick="ItemGridView_PreviewMouseDoubleClick">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Source="{Binding ItemImage}" Width="225" Height="157" Stretch="UniformToFill" StretchDirection="DownOnly" />
<StackPanel Margin="0,100,0,0">
<Border Margin="-0,-7,0,0" Height="63" Width="225" Background="{x:Null}" BorderBrush="{x:Null}" BorderThickness="0">
<TextBlock Margin="8" FontWeight="Heavy" Foreground="White" FontSize="16" Text="{Binding ItemName}"/>
</Border>
<TextBlock Margin="15,-28,0,0" FontSize="15" Text="{Binding SellingPrice}" Foreground="White"/>
</StackPanel>
</Grid>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The DataGrid to which the data is sent looks like this:
<DataGrid x:Name="DGItems" ItemsSource="{Binding}" VerticalAlignment="Top" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Row="0" MinHeight="350" MaxHeight="350" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" CanUserSortColumns="True" CanUserAddRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn MinWidth="3" Header="#" Width="Auto" Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Converter={global:RowToIndexConverter}}" />
<DataGridTextColumn Header="Items" Binding="{Binding ItemName}" />
<DataGridTextColumn Header="Cost" Binding="{Binding SellingPrice}" />
<DataGridTextColumn Header="Qty" />
</DataGrid.Columns>
</DataGrid>
The code behind to send the datan following a ListView double click event is as below:
private void ItemGridView_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var selectedItem = ItemGridView.SelectedItem;
if (!DGItems.Items.Contains(selectedItem))
{
DGItems.Items.Add(selectedItem);
}
else
{
utilityMethods.InformationMessage("Attempted to add item successfully");
}
}
I've included screenshots to preview how the application looks just to put the question in context.
Here's a working sample. I made slight modifications to your xaml and bindings to show how it can be done. Whenever you double-click an item in the ListView, it will update both the Quantity and Total columns in the DataGrid.
MyItem.cs: This is a simple model to replicate your food item
namespace UpdateQuantityColumnTest
{
public class MyItem
{
public string ItemName { get; set; }
public double SellingPrice { get; set; }
}
}
ListViewItemViewModel.cs: This is a view model representation of your MyItem for the ListView
namespace UpdateQuantityColumnTest
{
public class ListViewItemViewModel : ViewModelBase
{
public ListViewItemViewModel(MyItem model)
{
this.Model = model;
}
public MyItem Model { get; private set; }
public string ItemName { get => this.Model.ItemName; set { this.Model.ItemName = value; OnPropertyChanged(); } }
public string SellingPrice { get => this.Model.SellingPrice.ToString("c"); }
}
}
DGItemViewModel: This is a slightly different view model representation of your MyItem for the DataGrid, but includes a Quantity and a TotalPrice
namespace UpdateQuantityColumnTest
{
public class DGItemViewModel : ViewModelBase
{
private int quantity;
public DGItemViewModel(MyItem model)
{
this.Model = model;
this.quantity = 1; // always start at 1
}
public MyItem Model { get; private set; }
public string ItemName { get => this.Model.ItemName; set { this.Model.ItemName = value; OnPropertyChanged(); } }
public string SellingPrice { get => this.Model.SellingPrice.ToString("c"); }
public int Quantity { get => this.quantity; set { this.quantity = value; OnPropertyChanged(); OnPropertyChanged(nameof(TotalPrice)); } }
public string TotalPrice { get => (this.Model.SellingPrice * this.Quantity).ToString("c"); }
}
}
ViewModelBase.cs: This is the base class that simply handles the INotifyPropertyChanged to update the UI whenever one of the property values change in your view model
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace UpdateQuantityColumnTest
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MainWindow.xaml: I made slight changes, mainly to your bindings so that my sample could work
<Window x:Class="UpdateQuantityColumnTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView x:Name="ItemGridView" ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" PreviewMouseDoubleClick="ItemGridView_PreviewMouseDoubleClick">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Source="{Binding ItemImage}" Width="225" Height="157" Stretch="UniformToFill" StretchDirection="DownOnly" />
<StackPanel Margin="0,100,0,0">
<Border Margin="-0,-7,0,0" Height="63" Width="225" Background="{x:Null}" BorderBrush="{x:Null}" BorderThickness="0">
<TextBlock Margin="8" FontWeight="Heavy" Foreground="White" FontSize="16" Text="{Binding ItemName}"/>
</Border>
<TextBlock Margin="15,-28,0,0" FontSize="15" Text="{Binding SellingPrice}" Foreground="White"/>
</StackPanel>
</Grid>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<DataGrid x:Name="DGItems" ItemsSource="{Binding CheckoutItems}" VerticalAlignment="Top" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Grid.Row="1" MinHeight="350" MaxHeight="350" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" CanUserSortColumns="True" CanUserAddRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Items" Binding="{Binding ItemName}" />
<DataGridTextColumn Header="Cost" Binding="{Binding SellingPrice}" />
<DataGridTextColumn Header="Qty" Binding="{Binding Quantity}"/>
<DataGridTextColumn Header="Total" Binding="{Binding TotalPrice}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
MainWindow.xaml.cs: The code-behind that does all the work
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
namespace UpdateQuantityColumnTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<ListViewItemViewModel> items;
private ObservableCollection<DGItemViewModel> checkoutItems;
public MainWindow()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.DataContext = this;
}
public ObservableCollection<ListViewItemViewModel> Items
{
get
{
if (this.items == null)
this.items = new ObservableCollection<ListViewItemViewModel>();
return this.items;
}
}
public ObservableCollection<DGItemViewModel> CheckoutItems
{
get
{
if (this.checkoutItems == null)
this.checkoutItems = new ObservableCollection<DGItemViewModel>();
return this.checkoutItems;
}
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
// Populate with dummy data
this.Items.Add(new ListViewItemViewModel(new MyItem() { ItemName = "Beef Steak", SellingPrice = 1000 }));
this.Items.Add(new ListViewItemViewModel(new MyItem() { ItemName = "Bacon Brie", SellingPrice = 1200 }));
this.Items.Add(new ListViewItemViewModel(new MyItem() { ItemName = "Bread and Sausage", SellingPrice = 700 }));
}
private void ItemGridView_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var selectedItem = ItemGridView.SelectedItem as ListViewItemViewModel;
var checkoutItem = this.CheckoutItems.SingleOrDefault(o => o.ItemName == selectedItem.ItemName);
if (checkoutItem == null)
{
this.CheckoutItems.Add(new DGItemViewModel(selectedItem.Model));
}
else
{
//utilityMethods.InformationMessage("Attempted to add item successfully");
checkoutItem.Quantity++;
}
}
}
}
If the items in the ItemsSource = "{Binding Items}" collection have an INotifyPropertyChanged implementation and notify about the change of their properties, then you can solve this problem with minimal changes.
Add an int Qty property to the element class and change your method:
if (!DGItems.Items.Contains(selectedItem))
{
DGItems.Items.Add(selectedItem);
selectedItem.Qty = 1;
}
else
{
selectedItem.Qty++;
utilityMethods.InformationMessage("Attempted to add item successfully");
}
And add a binding to this property in the column:
<DataGrid.Columns>
<DataGridTextColumn MinWidth="3" Header="#" Width="Auto" Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Converter={global:RowToIndexConverter}}" />
<DataGridTextColumn Header="Items" Binding="{Binding ItemName}" />
<DataGridTextColumn Header="Cost" Binding="{Binding SellingPrice}" />
<DataGridTextColumn Header="Qty" Binding="{Binding Qty}" />
</DataGrid.Columns>
I have a program which so far follows MVVM principles/rules with 0 code behind and I have a DataGrid where users can add, edit or delete rows which represent students.
A user can add a row to the list by clicking on a "+" button but in order to edit the row the user has to first click on the row he just added which isn't so user-friendly.
I have been trying to set the newly added row in EditMode for a good while but all my attempts either failed or worked but with some disturbing side effects on the rest of the program. I looked it up online and the solutions I found either look like overkill or also have a bad side effect.
I created a similar program with less code to make it easier to show the structure of my program and DataGrid:
Model
public class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public string PhoneNumber { get; set; }
public string Address { get; set; }
public string Email { get; set; }
}
ViewModel
public class StudentsViewModel : INotifyPropertyChanged
{
public StudentsViewModel()
{
Students = new ObservableCollection<Student>();
}
private ObservableCollection<Student> students;
public ObservableCollection<Student> Students
{
get { return students; }
set
{
students = value;
NotifyPropertyChanged(nameof(Students));
}
}
private Student selectedStudent;
public Student SelectedStudent
{
get { return selectedStudent; }
set
{
selectedStudent = value;
NotifyPropertyChanged(nameof(SelectedStudent));
}
}
private ICommand addRow;
public ICommand AddRow
{
get
{
if(addRow == null)
{
addRow = new RelayCommand(
parameter => AddStudent(new Student()),
parameter => true
);
}
return addRow;
}
}
private ICommand removeCmd;
public ICommand RemoveCmd
{
get
{
removeCmd = new RelayCommand(
parameter => RemoveStudent(parameter as Student),
parameter => parameter != null
);
return removeCmd;
}
}
private void AddStudent(Student studentToAdd)
{
Students.Add(studentToAdd);
}
private void RemoveStudent(Student studentToRemove)
{
if (Students.Contains(studentToRemove))
{
Students.Remove(studentToRemove);
}
}
#region INotify
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
View
<Window x:Class="DataGridExample.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:DataGridExample"
mc:Ignorable="d"
Title="MainWindow"
Height="600"
Width="1000">
<Window.Resources>
<local:StudentsViewModel x:Key="StudentsVm"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource StudentsVm}}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<DockPanel LastChildFill="False"
Background="#FF2C58EC">
<Button Command="{Binding AddRow}"
Height="25"
Margin="5">
<Button.Template>
<ControlTemplate>
<Image Source="/Images/AddItem.png"/>
</ControlTemplate>
</Button.Template>
</Button>
<Button Command="{Binding RemoveCmd}"
CommandParameter="{Binding ElementName=StudentDataGrid, Path=SelectedItem}"
Height="25"
Margin="5">
<Button.Template>
<ControlTemplate>
<Image Source="/Images/DeleteItem.png"/>
</ControlTemplate>
</Button.Template>
</Button>
</DockPanel>
<DataGrid ItemsSource="{Binding Students}"
SelectedItem="{Binding Source={StaticResource StudentsVm}, Path=SelectedStudent, Mode=TwoWay}"
x:Name="StudentDataGrid"
ColumnWidth="*"
CanUserAddRows="False"
CanUserResizeRows="False"
CanUserResizeColumns="False"
CanUserSortColumns="False"
CanUserDeleteRows="False"
AutoGenerateColumns="False"
Grid.Row="1">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
Header="First Name">
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding LastName, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
Header="Last Name">
</DataGridTextColumn>
<DataGridTemplateColumn Header="Date of Birth">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding DateOfBirth, StringFormat={}{0:dd.MM.yyyy}, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding DateOfBirth, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
DisplayDate="{Binding DateOfBirth, Mode=OneWay, UpdateSourceTrigger=LostFocus}">
</DatePicker>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding PhoneNumber, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
Header="Phone Number">
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Address, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
Header="Address">
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Email, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
Header="Email">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
I would prefer the solution to be MVVM compatible but honestly at this point I would be satisfied as long as it doesn't cause other problems and doesn't require tons of frameworks or multiples files full of code.
I would prefer the result to look like this example but this is also acceptable as long as no mouse movement is involved after the "+" button click.
I found a way to achieve what you want but it doesn't seem very robust so use with care. First, you should hookup to the LoadingRow event of your DataGrid.
XAML
<DataGrid ...
LoadingRow="StudentDataGrid_LoadingRow"
...>
Code-behind
private async void StudentDataGrid_LoadingRow(object sender, DataGridRowEventArgs e) {
// Force asynchrony to run callback after UI has finished loading.
await Task.Yield();
// Mark the new row as selected
StudentDataGrid.SelectedItem = e.Row.Item;
StudentDataGrid.CurrentCell = new DataGridCellInfo(e.Row.Item, StudentDataGrid.Columns[0]);
// Enter edit mode
StudentDataGrid.BeginEdit();
}
By making the method async and by forcing it to execute asynchronously through the await Task.Yield() call, you let the UI finish the loading of the row before asking it to start an edit through the BeginEdit() call.
I'm writing this more as an experiment and I don't know if I would recommend this but I hope it helps while someone finds a better solution.
I am trying to bound some properties from my DataContext to my DataGrid without success...
The DataGridComboBox is empty and invisible before clicking on it, and I have two rows instead of one for the following source code.
The XAML
<DataGrid Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding ClassRow}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Classe" x:Name="Class_ClassName" Width="100" SelectedItemBinding="{Binding ClassName, Mode=TwoWay}" ItemsSource="{Binding ClassList}"/>
<DataGridTemplateColumn Header="Niveau">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox x:Name="LevelTxt" Width="50" TextChanged="LevelTxtTextChanged" Text="{Binding ClassLevel, TargetNullValue={x:Static sys:String.Empty}}"/>
<StackPanel Orientation="Vertical">
<Button x:Name="LevelUp" Content="+" Width="15" Height="15" Click="LevelUpClick" FontSize="10" VerticalContentAlignment="Top" />
<Button x:Name="LevelDown" Content="-" Width="15" Height="15" Click="LevelDownClick" FontSize="12" VerticalContentAlignment="Bottom"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
The DataContext :
public class ClassRow
{
public String ClassName;
public int ClassLevel;
}
public class PJDataWindow : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected List<ClassRow> m_classRow;
protected List<String> m_classList;
public PJDataWindow()
{
m_classRow = new List<ClassRow>();
m_classList = new List<String>();
//Test
m_classList.Add("Classe1");
m_classRow.Add(new ClassRow { ClassName = "Classe1", ClassLevel = 2 });
OnPropertyChanged("ClassList");
OnPropertyChanged("ClassRow");
}
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public List<ClassRow> ClassRow
{
get
{
return m_classRow;
}
set
{
m_classRow = value;
OnPropertyChanged("ClassRow");
}
}
public List<String> ClassList { get => m_classList; set { m_classList = value; OnPropertyChanged("ClassList"); } }
}
I am new in WPF and my search has led me to nowhere...
Thank you a lot !
There are 3 problems (so far).
The easiest problem is the two rows instead of one. The extra row is the new item row. Turn this off like so...
<DataGrid Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding ClassRow}" AutoGenerateColumns="False" Margin="40" CanUserAddRows="False">
The next problem is the empty text field. You can only bind to properties not fields. So to fix this change the fields in the row object to properties.
public class ClassRow
{
public String ClassName { get; set; }
public int ClassLevel { get; set; }
}
Finally the reason the combo box is empty is that the items source is not bound to the data context. The data grid column is outside the visual tree. So it cannot find the source. Fix it like this.
<FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
<DataGrid Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding ClassRow}" AutoGenerateColumns="False" Margin="40" CanUserAddRows="False" x:Name="dataGrid">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Classe" x:Name="Class_ClassName" Width="100" SelectedItemBinding="{Binding ClassName, Mode=TwoWay}" ItemsSource="{Binding Source={x:Reference dummyElement}, Path=DataContext.ClassList}"/>