C# - Popup is not displayed in second tab item in MVVM - c#

I'm working in MVVM and i have two TabItem created in XAML. In the first one, the popup is displayed, but in the second one when I press the button corresponding to a column, the popup is not displayed.
Here is my code with the working Popup:
<Viewbox>
<Grid Height="359" Width="746">
<Popup Name="popupFilter" Placement="MousePoint" IsOpen="{Binding IsFilterOpen, Mode=OneWay}" StaysOpen="True" Width="200">
<Border Background="White" BorderBrush="Gray" BorderThickness="1,1,1,1">
<StackPanel Margin="5,5,5,15">
<ListBox x:Name="listBoxPopupContent"
Height="250"
ItemsSource="{Binding FilterItems}"
BorderThickness="0"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Item}"
Command="{Binding DataContext.ApplyFiltersCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBox}}}"
CommandParameter="{Binding IsChecked,
RelativeSource={RelativeSource Self},
Mode=OneWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Border>
</Popup>
<Grid HorizontalAlignment="Left" Height="261" Margin="0,63,0,0" VerticalAlignment="Top" Width="736">
<TabControl HorizontalAlignment="Left" Height="175" VerticalAlignment="Top" Width="716" Margin="10,0,0,0">
<TabItem Header="Class">
<DataGrid x:Name="ClassViewDataGrid" ItemsSource="{Binding FilteredClassViewItems}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ClassName}">
<DataGridTextColumn.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Class" />
<Button Name="buttonClassViewClassFilter" Margin="10,0,0,0"
Command="{Binding DataContext.ShowFilterCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding Name, RelativeSource={RelativeSource Self}}">
<Button.ContentTemplate>
<DataTemplate>
<Image Source="/Images/filter.png" Width="10" Height="10" />
</DataTemplate>
</Button.ContentTemplate>
</Button>
</StackPanel>
</DataGridTextColumn.Header>
and the tabItem that doesn't shows the popup:
<TabItem Header="Field">
<DataGrid x:Name="FielsdViewDataGrid" ItemsSource="{Binding FilteredFieldViewItems}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ClassName}">
<DataGridTextColumn.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Class" />
<Button Name="buttonFieldViewClassFilter" Margin="10,0,0,0"
Command="{Binding DataContext.ShowFilterCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding Name, RelativeSource={RelativeSource Self}}">
<Button.ContentTemplate>
<DataTemplate>
<Image Source="/Images/filter.png" Width="10" Height="10" />
</DataTemplate>
</Button.ContentTemplate>
</Button>
</StackPanel>
</DataGridTextColumn.Header>
...
The second tabItem is defined like the first one, but it seems that the Binding DataContext.ShowFilterCommand is not reached. I tried to enter with debug there but is not reached.
Here is the method:
private void ShowFilterCommandRaised(object obj)
{
IsFilterOpen = !IsFilterOpen;
str = obj;
if (IsFilterOpen)
{
if (str.Equals("buttonClassViewClassFilter"))
{
FilterItems.Clear();
foreach (var classView in classViewItems)
{
FilterItems.Add(new CheckedListItem<string>(classView.ClassName, true));
}
}
if (str.Equals("buttonClassViewExtendsFilter"))
{
FilterItems.Clear();
foreach (var classView in classViewItems)
{
FilterItems.Add(new CheckedListItem<string>(classView.Category, true));
}
}
if (str.Equals("buttonFieldViewClassFilter"))
{
FilterItems.Clear();
foreach (var fieldView in fieldViewItems)
{
FilterItems.Add(new CheckedListItem<string>(fieldView.ClassName, true));
}
}
}
What am i doing wrong?

If you swap the order of the two TabItems, or set SelectedIndex="1" on the TabControl, you'll find that the one that's initially visible is always the only one that works. I added PresentationTraceSources.TraceLevel=High to the Command binding, and found that the binding in the TabItem that's hidden on startup tries to resolve its source property initially, and doesn't try again when that TabItem becomes active.
The trouble is that at the time of that first attempt at resolving the binding in the hidden tab item, none of the UI for that TabItem actually exists yet. Due to virtualization, it won't be created until it's needed. All that's created is its Header content, hanging in space. The DataGrid doesn't exist yet, so the AncestorType search never finds it. When the DataGrid is finally created, it seems there is no event that's raised to notify the Binding that it needs to repeat the ancestor search.
The fix for this is easy: Create the header content via a DataTemplate. That's the correct way to do it anyhow. The DataTemplate will be instantiated when the DataGrid is shown.
We'll use a value converter to create an identifier which tells the command what grid and column the user clicked. Notice that we're now giving each column a plain string for its Header property, and the TextBlock in the template changes to <TextBlock Text="{Binding}" />.
XAML
<TabControl HorizontalAlignment="Left" Height="175" VerticalAlignment="Top" Width="716" Margin="10,0,0,0">
<TabControl.Resources>
<local:GetColumnIdentifier x:Key="GetColumnIdentifier" />
<DataTemplate x:Key="FilterColumnHeaderTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<Button
Margin="10,0,0,0"
Command="{Binding DataContext.ShowFilterCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding Converter={StaticResource GetColumnIdentifier}, RelativeSource={RelativeSource Self}}"
>
<Button.ContentTemplate>
<DataTemplate>
<Image Source="/Images/filter.png" Width="10" Height="10" />
</DataTemplate>
</Button.ContentTemplate>
</Button>
</StackPanel>
</DataTemplate>
</TabControl.Resources>
<TabItem Header="Class">
<DataGrid
x:Name="ClassViewDataGrid"
ItemsSource="{Binding FilteredClassViewItems}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True"
>
<DataGrid.Columns>
<DataGridTextColumn
Header="Class"
Binding="{Binding ClassName}"
HeaderTemplate="{StaticResource FilterColumnHeaderTemplate}"
/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<TabItem Header="Field">
<DataGrid
x:Name="FielsdViewDataGrid"
ItemsSource="{Binding FilteredFieldViewItems}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True"
>
<DataGrid.Columns>
<DataGridTextColumn
Header="Class"
Binding="{Binding ClassName}"
HeaderTemplate="{StaticResource FilterColumnHeaderTemplate}"
/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
</TabControl>
Value converter:
//using System.Windows.Markup.Primitives;
//using System.Windows.Controls.Primitives;
public class GetColumnIdentifier : IValueConverter
{
private static T GetVisualAncestor<T>(DependencyObject obj)
where T : DependencyObject
{
while (obj != null)
{
if (obj is T)
return obj as T;
else
obj = VisualTreeHelper.GetParent(obj);
}
return null;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Not all DataGridColumn subclasses have a Binding property.
var header = GetVisualAncestor<DataGridColumnHeader>((DependencyObject)value);
var datagrid = GetVisualAncestor<DataGrid>(header);
if (header?.Column != null)
{
MarkupObject markupObject = MarkupWriter.GetMarkupObjectFor(header.Column);
var bindingProp = markupObject.Properties.FirstOrDefault(p => p.Name == "Binding");
if (bindingProp?.Value is Binding binding)
{
return $"{datagrid?.Name}.{binding.Path.Path}";
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Here's another solution if you have multiple columns bound to the same path, and you need to distinguish among them (in that case though, you could use the above solution for most of them, but slip in a customized template for the odd special case):
<TabControl HorizontalAlignment="Left" Height="175" VerticalAlignment="Top" Width="716" Margin="10,0,0,0">
<TabItem Header="Class">
<DataGrid
x:Name="ClassViewDataGrid"
ItemsSource="{Binding FilteredClassViewItems}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True"
>
<DataGrid.Columns>
<DataGridTextColumn
Header="Class"
Binding="{Binding ClassName}"
>
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<Button
Name="buttonClassViewClassFilter"
Margin="10,0,0,0"
Command="{Binding DataContext.ShowFilterCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding Name, RelativeSource={RelativeSource Self}}"
>
<Button.ContentTemplate>
<DataTemplate>
<Image Source="/Images/filter.png" Width="10" Height="10" />
</DataTemplate>
</Button.ContentTemplate>
</Button>
</StackPanel>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<TabItem Header="Field">
<DataGrid
x:Name="FielsdViewDataGrid"
ItemsSource="{Binding FilteredFieldViewItems}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True"
>
<DataGrid.Columns>
<DataGridTextColumn
Header="Class"
Binding="{Binding ClassName}"
>
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<Button
Name="buttonFieldViewClassFilter"
Margin="10,0,0,0"
Command="{Binding DataContext.ShowFilterCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding Name, RelativeSource={RelativeSource Self}}"
>
<Button.ContentTemplate>
<DataTemplate>
<Image Source="/Images/filter.png" Width="10" Height="10" />
</DataTemplate>
</Button.ContentTemplate>
</Button>
</StackPanel>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</TabItem>
</TabControl>
Original Version
It turns out that OP has multiple filtered columns per grid, so the solution below won't work: The command needs to know not just which grid, but which column in the grid. DataGridColumn isn't in the visual tree so we can't use that x:Name for the command parameter. We could use {RelativeSource AncestorType=DataGridColumnHeader} and write a converter that returns DataGridColumnHeader.Column.Binding.Path.Path -- but both grids have columns named ClassName. Next up is a multibinding that passes that ancestor, and also the grid itself. Since OP's original solution involved distinct content per column header anyway, I decided to do it the easy but verbose way, with multiple templates.
I made one important change that will affect your code: Since the header content for both DataGrid columns is now being created by the same template, the name of the button is now the same for both. Therefore, CommandParameter is now bound to the name of the DataGrid.
<TabControl HorizontalAlignment="Left" Height="175" VerticalAlignment="Top" Width="716" Margin="10,0,0,0">
<TabControl.Resources>
<DataTemplate x:Key="FilterColumnHeaderTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<Button
Name="buttonClassFilter"
Margin="10,0,0,0"
Command="{Binding DataContext.ShowFilterCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding Name, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
>
<Button.ContentTemplate>
<DataTemplate>
<Rectangle Fill="DeepSkyBlue" Width="10" Height="10" />
</DataTemplate>
</Button.ContentTemplate>
</Button>
</StackPanel>
</DataTemplate>
</TabControl.Resources>
<TabItem Header="Class">
<DataGrid
x:Name="ClassViewDataGrid"
ItemsSource="{Binding FilteredClassViewItems}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True"
>
<DataGrid.Columns>
<DataGridTextColumn
Header="Class"
Binding="{Binding ClassName}"
HeaderTemplate="{StaticResource FilterColumnHeaderTemplate}"
/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<TabItem Header="Field">
<DataGrid
x:Name="FielsdViewDataGrid"
ItemsSource="{Binding FilteredFieldViewItems}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True"
>
<DataGrid.Columns>
<DataGridTextColumn
Header="Class"
Binding="{Binding ClassName}"
HeaderTemplate="{StaticResource FilterColumnHeaderTemplate}"
/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
</TabControl>
If you want to vary the header content between the columns in the two different DataGrids, you can do that with bindings or create a second template if necessary.
You could have also done this with a BindingProxy created in TabControl.Resources, but binding proxies are a last-ditch kludge that we use when there's no "correct" way to do something. In this case, the "correct" way is easy and direct and perfectly satisfactory.

Related

Bind to an ObservableCollection which is inside another ObservableCollection

]3
So i have an ObservableCollection with movies in my ViewModel, and inside that ObservableCollection there's another ObservableCollection with all the actors that participate in the movie. I wanted to show that Actors ObservableCollection in my DataGrid, but i don't know how to do it, do you have any idea? Thanks
<controls:DataGrid
Grid.Row="1"
x:Name="viewFilmes"
Margin="12"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"
AlternatingRowBackground="Transparent"
AlternatingRowForeground="Gray"
AreRowDetailsFrozen="False"
AreRowGroupHeadersFrozen="True"
AutoGenerateColumns="False"
CanUserSortColumns="False"
CanUserReorderColumns="True"
CanUserResizeColumns="False"
ColumnHeaderHeight="32"
MaxColumnWidth="400"
FrozenColumnCount="0"
GridLinesVisibility="None"
HeadersVisibility="Column"
RowDetailsVisibilityMode="Collapsed"
SelectionChanged="viewFilmes_SelectionChanged"
SelectionMode="Single"
RowEditEnded="viewFilmes_RowEditEnded"
RowGroupHeaderPropertyNameAlternative="Range"
ItemsSource="{x:Bind GestaoDeFilmesViewModel.Filmes}">
<controls:DataGrid.Columns>
<controls:DataGridTextColumn Header="Id" IsReadOnly="True" Binding="{Binding Idfilme, Mode=OneWay}" Tag="Id"/>
<controls:DataGridTextColumn Header="Nome" Binding="{Binding Nome, Mode=TwoWay}" Tag="Nome" />
<controls:DataGridTextColumn Header="Ano" Binding="{Binding Ano, Mode=TwoWay}" Tag="Ano" />
<controls:DataGridTextColumn Header="Duracao" Binding="{Binding Duracao, Mode=TwoWay}" Tag="Duracao" />
<controls:DataGridTemplateColumn Header="Foto" Tag="Foto" >
<controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<control:FotoControl ImageSource="{Binding Foto, Mode=TwoWay}"></control:FotoControl>
<Button Click="Foto_Button_Click">Alterar Foto</Button>
</StackPanel>
</DataTemplate>
</controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>
<controls:DataGridTextColumn Header="Genero" Tag="Genero"/>
<controls:DataGridTextColumn Header="Diretor" Binding="{Binding Diretor.Nome, Mode=TwoWay}" Tag="Diretor"/>
<controls:DataGridTextColumn Header="Escritor" Binding="{Binding Escritor.Nome, Mode=TwoWay}" Tag="Escritor"/>
<controls:DataGridTemplateColumn Header="Elenco" Tag="Elenco">
<controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Ver Elenco">
<Button.Flyout>
<Flyout>
<StackPanel>
<TextBlock Style="{ThemeResource BaseTextBlockStyle}" Text="Elenco" Margin="0,0,0,12" />
<!--<TextBox Text="{Binding Atores.Idator, Mode=TwoWay}"/>-->
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</DataTemplate>
</controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>
</controls:DataGrid.Columns>
</controls:DataGrid>
If you plan to display collection content, UWP provides GridView and ListView controls.
like this:
xaml.cs
public ObservableCollection<string> TestCollection {get; set;}
xaml
<GridView ItemsSource="{x:Bind TestCollection}"/>
If you plan to display a ListView or GridView in the DataGrid, you can try to put the control in RowDetail. like this:
<controls:DataGrid ...>
...
<controls:DataGrid.RowDetailsTemplate>
<DataTemplate>
<Grid>
<GridView ItemsSource="{Binding Atores}">
</GridView>
</Grid>
</DataTemplate>
</controls:DataGrid.RowDetailsTemplate>
...
</controls:DataGrid>
Here are some documents that might help you:
ListView and GridView
How to: Display and Configure Row Details in the DataGrid Control
Best regards.

How can I make a ComboBox in a DataGrid row unique?

Environment for the following question:
C#, WPF, XAML
How can I achieve to setup a table/a DataGrid with an undefined number of rows having a Combobox in an arbitrary column (number of columns is undefined as well)? (The other row's cells will be filled with the properties of the underyling object/entity.) The ComboBoxes shall all have the same items but the selected item shall be assigned to the underlying object of the row (of course). Hence, I think the ComboBox of each row must have a unique identifier.
The selected item shall be stored in a property of the underlying object/entity.
By the way:
The ComboBox shall be filled with items of a collection (List) that is not part of the row's underlying object/entity.
What would be the best way using WPF / XAML?
UPDATE (2018-12-14):
<Window x:Class="ConfigTool.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:ConfigTool"
xmlns:lb="clr-namespace:ConfigTool.DataBinding"
mc:Ignorable="d"
Title="xxx" Height="650" Width="1200" Closing="CloseWindow">
<Window.Resources>
<CollectionViewSource x:Key="TagsCollectionViewSource" CollectionViewType="ListCollectionView"/>
<CollectionViewSource x:Key="NotificationsCollectionViewSource" CollectionViewType="ListCollectionView"/>
<CollectionViewSource x:Key="TagNamesCollectionViewSource" CollectionViewType="ListCollectionView"/>
<lb:StringListConverter x:Key="StringListConverter" />
</Window.Resources>
<Grid Margin="10,10,10,10">
<TabControl>
<TabItem Header="Tags">
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<DataGrid x:Name="tagsGrid" DataContext="{StaticResource TagsCollectionViewSource}" ItemsSource="{Binding}"
AlternatingRowBackground="LightBlue" AutoGenerateColumns="False" CanUserAddRows="True" IsReadOnly="False"
SelectionMode="Single" BorderBrush="Magenta" BorderThickness="3">
<DataGrid.Columns>
<DataGridTextColumn x:Name="TagName" Header="Tag name" Binding="{Binding Mode=TwoWay, Path=TagName}"></DataGridTextColumn>
<DataGridTextColumn Header="Cycle" Binding="{Binding Mode=TwoWay, Path=Cycle}"></DataGridTextColumn>
<DataGridTextColumn Header="Source" Binding="{Binding Mode=TwoWay, Path=Source}"></DataGridTextColumn>
<DataGridTemplateColumn x:Name="editTagColumn" Header="Edit">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<WrapPanel>
<Button x:Name="btnTagDelete" Click="BtnTagDelete_Click" CommandParameter="{Binding}" Height="15" Width="15" Margin="2">
<Button.Content>
<Image Source="Resources/delete.png"></Image>
</Button.Content>
</Button>
</WrapPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
</TabItem>
<TabItem Header="Notifications">
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<DataGrid x:Name="notificationsGrid" DataContext="{StaticResource NotificationsCollectionViewSource}" ItemsSource="{Binding}"
AlternatingRowBackground="LightBlue" AutoGenerateColumns="False" CanUserAddRows="True" IsReadOnly="False"
SelectionMode="Single" BorderBrush="Magenta" BorderThickness="3">
<DataGrid.Columns>
<!--<DataGridTextColumn Header="Tag name" Binding="{Binding Mode=TwoWay, Path=TagName}"></DataGridTextColumn>-->
<DataGridTemplateColumn x:Name="tagNameColumn" Header="Tag name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!--<ItemContainerTemplate>
<StackPanel>-->
<!--DataContext="{Binding Source={StaticResource TagNamesCollectionViewSource}}">-->
<!--<ComboBox Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding TagNames, Converter={StaticResource StringListConverter}}">-->
<ComboBox Name="notificationTagName" Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding Source={StaticResource TagNamesCollectionViewSource}, RelativeSource={RelativeSource AncestorType=local:MainWindow}}"
SelectionChanged="notificationTagName_SelectionChanged" />
<!--<ComboBox Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding Converter={StaticResource StringListConverter}}" />-->
<!--<ComboBox Width="200" ItemsSource="{Binding ElementName=Window2, Path=DataContext.TagNames, Converter={StaticResource StringListConverter}}" />-->
<!--<ComboBox HorizontalAlignment="Left" Margin="256,260,0,0" VerticalAlignment="Top" Width="120" x:Name="DataList" ItemsSource="{Binding DetailParams, Converter={StaticResource StringListConverter}}"/>-->
</DataTemplate>
<!--</StackPanel>
</ItemContainerTemplate>-->
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridCheckBoxColumn Header="IsActive" Binding="{Binding Mode=TwoWay, Path=IsActive}"></DataGridCheckBoxColumn>
<DataGridTextColumn Header="Type" Binding="{Binding Mode=TwoWay, Path=Type}"></DataGridTextColumn>
<DataGridTextColumn Header="Limit" Binding="{Binding Mode=TwoWay, Path=Limit}"></DataGridTextColumn>
<DataGridTextColumn Header="DetailTemplate" Binding="{Binding Mode=TwoWay, Path=DetailTemplate}"></DataGridTextColumn>
<!--<DataGridTextColumn Header="DetailParams" Binding="{Binding Mode=TwoWay, Path=DetailParams, Converter={StaticResource StringListConverter}}"></DataGridTextColumn>-->
<DataGridTemplateColumn x:Name="detailsParamColumn" Header="Edit">
<DataGridTemplateColumn.CellTemplate>
<ItemContainerTemplate>
<StackPanel> <!--DataContext="{Binding Source={StaticResource TagNamesCollectionViewSource}}">-->
<!--<ComboBox Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding TagNames, Converter={StaticResource StringListConverter}}">-->
<ComboBox Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding}" />
<!--<ComboBox Width="200" DataContext="{StaticResource TagNamesCollectionViewSource}" ItemsSource="{Binding Converter={StaticResource StringListConverter}}" />-->
<!--<ComboBox Width="200" ItemsSource="{Binding ElementName=Window2, Path=DataContext.TagNames, Converter={StaticResource StringListConverter}}" />-->
<!--<ComboBox HorizontalAlignment="Left" Margin="256,260,0,0" VerticalAlignment="Top" Width="120" x:Name="DataList" ItemsSource="{Binding DetailParams, Converter={StaticResource StringListConverter}}"/>-->
</StackPanel>
</ItemContainerTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="CauseProbability" Binding="{Binding Mode=TwoWay, Path=CauseProbability, Converter={StaticResource StringListConverter}}"></DataGridTextColumn>
<DataGridTemplateColumn x:Name="editNotificationsColumn" Header="Edit">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<WrapPanel>
<Button x:Name="btnNotificationDelete" Click="BtnNotificationDelete_Click" Height="15" Width="15" Margin="2">
<Image Source="Resources/delete.png"></Image>
</Button>
</WrapPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
</TabItem>
</TabControl>
</Grid>
</Window>
It is happening because you are using the same collection for the Combo-Box in each row.
The ItemsSource for the Combo-Box should be a collection and it should be a member/property of class that set as DataContext for the DataGrid.
Or
Bind the SelectedItem property of Combo-Box to a member of your class.
See https://www.c-sharpcorner.com/uploadfile/dpatra/combobox-in-datagrid-in-wpf/

WPF DataGridCell of wrong type

I have a WPF datagrid that isn't behaving. When I click to edit the cell, the DataGridTemplateColumn.CellEditingTemplate is not displayed. If I click inside the cell and hit F2 to edit, the DataGridTemplateColumn.CellEditingTemplate is displayed as it should be. I was trying to make this a one click edit cell using the code from this SO post.
private void PendingDetailsDataGrid_GotFocus( object sender, RoutedEventArgs e )
{
if ( e.OriginalSource.GetType() == typeof( DataGridCell ) )
{
DataGrid grd = ( DataGrid ) sender;
grd.BeginEdit( e );
}
}
and I noticed that the if statement always fails because DataGridCell is of type Microsoft.Windows.Controls using assembly (WPFToolkit.dll) and the e.OriginalSource is of type System.Windows.Controls (Presentation.dll). If I hover over the DataGrid declaration in XAML view it shows System.Windows.Controls.DataGrid so how is the DataGridCell of the wrong type? I have an almost identical setup in another Window and I copied the DataGrid and the references from that form but nothing changed.
EDIT
<Controls:MetroWindow
x:Class="DFTI.Views.TransactionsWindow2"
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:Controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:vm="clr-namespace:DFTI.ViewModels"
xmlns:local="clr-namespace:DFTI.Views"
xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit"
xmlns:Dialog="clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform"
Dialog:DialogParticipation.Register="{Binding}"
Title="Transactions" Height="560" Width="900"
Name="Transactions"
WindowStartupLocation="CenterScreen">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<cmd:EventToCommand Command="{Binding Path=ClosingCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<DataGrid Grid.Row="0"
x:Name="PendingTransactions"
DataGridCell.Selected="PendingDetailsDataGrid_GotFocus"
ItemsSource="{Binding PendingTransactions}"
SelectedItem="{Binding PendingDetails}"
AutoGenerateColumns="False"
CanUserResizeColumns="True"
CanUserDeleteRows="False"
CanUserReorderColumns="True"
Margin="10,0,10,17" >
<DataGrid.Columns>
<DataGridTextColumn Header="Posting Date" Width="Auto" Binding="{Binding PostingDate}" IsReadOnly="True" />
<DataGridTextColumn Header="Status" Width="Auto" Binding="{Binding Status}" IsReadOnly="True" />
<DataGridTextColumn Header="Batch No" Width="Auto" Binding="{Binding BatchNo}" IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
<DataGrid Grid.Row="1"
Height="200"
ItemsSource="{Binding PendingDetails.Details}"
AutoGenerateColumns="False"
CanUserAddRows="True"
CanUserDeleteRows="True"
CanUserResizeColumns="True"
CanUserReorderColumns="True"
DataGridCell.Selected="PendingDetailsDataGrid_GotFocus"
Margin="10,0">
<DataGrid.Columns>
<DataGridTemplateColumn x:Name="AccountNum" Header="Account No" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBox BorderThickness="0" Text="{Binding Path=Account.RawAccountNumber}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel>
<toolkit:AutoCompleteBox
Background="AliceBlue"
IsTextCompletionEnabled="True"
FilterMode="Contains"
MinimumPrefixLength="2"
ValueMemberPath="RawAccountNumber"
PreviewTextInput="AutoCompleteBox_PreviewTextInput"
SelectedItem="{Binding Account, Mode=TwoWay}"
Text="{Binding Path=Account.RawAccountNumber}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Accounts}" >
<toolkit:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Path=RawAccountNumber}" FontWeight="Bold" Width="100"/>
<TextBlock Text="{Binding Path=Description}" />
</StackPanel>
</DataTemplate>
</toolkit:AutoCompleteBox.ItemTemplate>
</toolkit:AutoCompleteBox>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Description" Width="Auto" Binding="{Binding Description}" IsReadOnly="True" />
<DataGridTemplateColumn x:Name="DebitAmt" Header="Debit">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding DAmount, StringFormat='$#,##0.00;$(#,##0.00)'}"
Width="100"
BorderThickness="0">
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Controls:NumericUpDown Value="{Binding DAmount, Mode=TwoWay}" StringFormat="C2" Minimum="0" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn x:Name="CreditAmt" Header="Credit">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding CAmount, StringFormat='$#,##0.00;$(#,##0.00)'}"
Width="100"
BorderThickness="0">
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Controls:NumericUpDown Value="{Binding CAmount, Mode=TwoWay}" StringFormat="C2" Minimum="0" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Controls:MetroWindow >
Trt to use the fully qualified type name in the if statement. You should not need to involve WPFToolkit.dll here. This works for me:
private void PendingDetailsDataGrid_GotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource.GetType() == typeof(System.Windows.Controls.DataGridCell))
{
DataGrid grd = (DataGrid)sender;
grd.BeginEdit(e);
}
}

Fit an Picture in a DataGrid

I'm trying to put a picture in my DataGrid. So I used a DataGridTemplateColumn but the Result is "glitchy".
Here is my wpf extract code :
<DataGrid MinRowHeight="24" x:Name="ListFamily" Grid.Column="1"
Margin="2" VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"
AutoGenerateColumns="False" IsReadOnly="True"
SelectionChanged="ListFamily_SelectionChanged"
CanUserReorderColumns="False"
CanUserSortColumns="False" Grid.Row="1">
<DataGrid.Columns>
<!-- some datagridcolumn -->
<DataGridTemplateColumn Width="Auto" CanUserResize="False" IsReadOnly="True">
<DataGridTemplateColumn.Header>
<Label Content="{x:Static res:StringResources.language}" ToolTip="{x:Static tt:TooltipStrings.language}" HorizontalContentAlignment="Stretch"/>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Language, Converter={StaticResource StringToPicture}}" Height="{Binding RowHeight, ElementName=ListFamily}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stretch="Uniform"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style>
<Setter Property="FrameworkElement.VerticalAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
and here is what I get :
I triend to bind RowHeight of the DataGrid but without luck. Any ideas ?
Thanks
Thibaud
Using this code is working :
<DataGridTemplateColumn Width="Auto" CanUserResize="False" IsReadOnly="True">
<DataGridTemplateColumn.Header>
<Label Content="{x:Static res:StringResources.language}" ToolTip="{x:Static tt:TooltipStrings.language}" HorizontalContentAlignment="Stretch"/>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Language, Converter={StaticResource StringToPicture}}" Stretch="Uniform"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Bind datagrid column visibility to a dictionary value

people! I have an issue:
i have an app on MVVM Light with DataGrid:
<DataGrid ItemsSource="{Binding Path=MyCollection, Mode=TwoWay}" AutoGenerateColumns="False" CanUserAddRows="False"
FontSize="14" Name="ItemGrid" SelectionUnit="FullRow" ColumnHeaderStyle="{StaticResource ResourceKey=DataGridColumnHeader}" Background="Transparent"
Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},UpdateSourceTrigger=PropertyChanged}" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="4"
CanUserResizeRows="False" SelectionMode="Single" BorderThickness="0">
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid
IsReadOnly="True"
AutoGenerateColumns="False"
ItemsSource="{Binding MyItems}"
SelectionUnit="FullRow"
Visibility="{Binding Path=DataContext.RowDetailsVisibility, Mode=TwoWay,Converter={StaticResource BoolToVisibilityConverter}, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
CanUserResizeRows="False"
CanUserResizeColumns="True"
SelectedItem="{Binding Path=DataContext.SelectedItem, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Visibility="Collapsed" Binding="{Binding ID}" Width="Auto" CellStyle="{StaticResource ResourceKey=DataGridCell}" />
<DataGridTextColumn Header="Title" Visibility="Visible" Binding="{Binding Title}" Width="Auto" CellStyle="{StaticResource ResourceKey=DataGridCell}" />
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Item Pack ID" Width="*" CellStyle="{StaticResource ResourceKey=DataGridCell}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Id, Mode=TwoWay}" Style="{StaticResource ResourceKey=TextBoxStyle}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Number" Width="*" CellStyle="{StaticResource ResourceKey=DataGridCell}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Number, Mode=TwoWay}" Style="{StaticResource ResourceKey=TextBoxStyle}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid.Columns>
<DataGrid.ContextMenu>
<ContextMenu Name="contextMenu" Style="{StaticResource ContextStyle}">
</MenuItem>
<MenuItem Header="Packs Columns"
ItemsSource="{Binding PacksCollection}"
DisplayMemberPath="Key"
Command="{Binding Path=DataContext.PacksCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
>
</MenuItem>
<MenuItem Header="Items Columns"
Command="{Binding Path=DataContext.ItemsCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
ItemsSource="{Binding ItemsCollection}" DisplayMemberPath="Key">
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Grid>
So, if you understand, i have a grid with Packs(Id,Number),each Pack have some Items(Id,Title). I want to have a context menu, which have 2 lists: Packs and Items. In each menu item we can choose what column to show.
ItemsCollection and PacksCollection are Dictionary where String - Column Header(same as in data grid) and bool - visibility state(true for Visible, false for Collapsed).
Question: how to bind MVVM click on menu item, that will change column visibility. I tried some solutions, but they don't worked for me.
I need to change visibility for DataGrid columns(Id,Number) and for RowDetails DataGrid - Id, Title. Help please, i don't think this is so hard.
Thank you.

Categories

Resources