How to stretch checkbox item the full width of the combobox - wpf

I have a multiply combobox with checkbox items
<ComboBox x:Name="cmb" IsEditable="True" IsReadOnly="True" DropDownClosed="cmb_DropDownClosed">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding NmColumn }" HorizontalAlignment="Stretch"
IsChecked="{Binding Path=bChecked, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Tag="{Binding IdColumn}"
/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
When I click on checkbox all is well. But if the checkbox width less than the width of combobox, when I click to the right of the checkbox, the combobox closes. how to override this behavior?

Set HorizontalContentAlignment to Stretch in ComboBox.ItemContainerStyle:
<ComboBox x:Name="cmb" IsEditable="True" IsReadOnly="True" DropDownClosed="cmb_DropDownClosed">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<CheckBox Content="{Binding NmColumn }" HorizontalAlignment="Stretch"
IsChecked="{Binding Path=bChecked, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Tag="{Binding IdColumn}"
/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>

The answer by Ramin is partially good. You aslo have to remove the <StackPanel Orientation="Horizontal"> from your data template.It's him that is restricting your checkbox from expanding on all ComboboxItem width.
<ComboBox x:Name="cmb" IsEditable="True" IsReadOnly="True" DropDownClosed="cmb_DropDownClosed">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding NmColumn }" HorizontalAlignment="Stretch"
IsChecked="{Binding Path=bChecked, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Tag="{Binding IdColumn}"
/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>

All Answers did not work for me. What did the Trick was setting
HorizontalContentAlignment="Stretch"
for the CheckBox:
<ComboBox x:Name="combobox"
Background="White"
Padding="2"
Text="{Binding ElementName=DockPanelTemplateComboCheck, Path=ComboTextFilter}"
IsEditable="True"
IsReadOnly="True"
HorizontalAlignment="Stretch"
ItemsSource="{Binding ...}"
IsDropDownOpen="{Binding Path=DropOpen, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Eintrag}" HorizontalContentAlignment="Stretch" Checked="CheckBox_Checked_Unchecked" Unchecked="CheckBox_Checked_Unchecked"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
CodeBehind:
private string combotextfilter = "<No Selection>";
public string ComboTextFilter
{
get { return combotextfilter; }
set
{
if (value != null && value.IndexOf("ComboModel") != -1) return;
combotextfilter = value;
NotifyPropertyChanged(nameof(ComboTextFilter));
}
}
private void CheckBox_Checked_Unchecked(object sender, RoutedEventArgs e)
{
switch (((ObservableCollection<ComboModel>)combobox.ItemsSource).Count(x => x.IsChecked))
{
case 0:
ComboTextFilter = "<No Selection>";
break;
case 1:
ComboTextFilter = ((ObservableCollection<ComboModel>)combobox.ItemsSource).Where(x => x.IsChecked).First().Eintrag;
break;
default:
ComboTextFilter = ((ObservableCollection<ComboModel>)combobox.ItemsSource).Where(x => x.IsChecked).Select(x => x.Eintrag).Aggregate((i, j) => i + " | " + j);
//ComboTextFilter = "<Multiple Selected>";
break;
}
NotifyPropertyChanged(nameof(C_Foreground));
}
public bool DropOpen
{
get { return dropopen; }
set { dropopen = value; NotifyPropertyChanged(nameof(ComboTextFilter)); }
}
private bool dropopen = false;
Note: This ComboBox also changes the text of the ComboBox depending on the elements selected.

Related

Filter DataGrid columns and pass object to ViewModel

I get this error :
System.Windows.Data Error: 40 : BindingExpression path error: 'OpenPopupCommand' property not found on 'object' ''String' (HashCode=62725275)'. BindingExpression:Path=OpenPopupCommand; DataItem='String'
when I added a parameter to my command:
OpenPopupCommand = new RelayParamCommand((e) => PopupVisibility(FilterButton) );
VM:
private void PopupVisibility(object sender)
{
Console.WriteLine(sender.ToString());
PopupVisible ^= true;
}
Think is that I added Filter Button to Datagrid Headers which are generated automatically. Now I want to open popup when button is clicked. But think is that is not working, because i have to pass button by buttons x:Name to Popup PlacementTarget parameter.
<Page.DataContext>
<PDB:UsersViewModel x:Name="vm"/>
</Page.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--Page Header info content-->
<Grid Grid.Row="0">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="2" Text="{Binding ElementName=userPage, Path=Name}"/>
<TextBlock Margin="2" Text="{Binding SelectedUser.Name}"/>
<TextBlock Margin="2" Text="{Binding ElementName=myGrd, Path=CurrentColumn.DisplayIndex}"/>
<Button x:Name="mybtn"
Content="{Binding Filters.Count, Mode=OneWay}"
Visibility="{Binding Filters.Count, Converter={Wpf:VisibilityConverter}}"
/>
</StackPanel>
</Grid>
<!--Datagrid content-->
<DataGrid x:Name="myGrd"
SelectionMode="Single"
SelectionUnit="Cell"
CurrentItem="{Binding SelectedUser, Mode=TwoWay}"
CurrentColumn="{Binding CurrentColumn, Mode=TwoWay}"
IsReadOnly="True"
Grid.Row="1"
ItemsSource="{Binding FilteredUserList}"
AutoGenerateColumns="True"
CanUserAddRows="False"
>
<DataGrid.Resources>
<!--Popup-->
<ContextMenu x:Key="ContextMenu">
<ContextMenu.Items>
<MenuItem Header="Filter by Selection" Command="{Binding IncludeCommand, Source={x:Reference vm}}"/>
<MenuItem Header="Filter exclude Selection" Command="{Binding ExcludeCommand, Source={x:Reference vm}}"/>
<MenuItem Header="Remove all Filters" Command="{Binding RemoveAllFiltersCommand, Source={x:Reference vm}}" Visibility="{Binding Filters.Count, Source={x:Reference vm}, Converter={Wpf:VisibilityConverter}}"/>
</ContextMenu.Items>
</ContextMenu>
<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader" x:Name="FilterHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Margin="0,0,0,10" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" HorizontalAlignment="Center"/>
<Button Name="FilterButton"
Content="[-F-]"
Command="{Binding OpenPopupCommand}"
CommandParameter="{Binding ElementName=FilterButton}"/>
<Popup Name="MyPopup"
StaysOpen="False"
Placement="Right"
IsOpen="{Binding PopupVisible}"
PlacementTarget="{Binding FilterButton}">
<Border Background="White" BorderBrush="Black" Padding="5" BorderThickness="2" CornerRadius="5">
<StackPanel Orientation="Vertical">
</StackPanel>
</Border>
</Popup>
</StackPanel>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
</Style>
</DataGrid.CellStyle>
</DataGrid>
</Grid>
By this approach i want to pass clicked button as parameter to VM and bind it to Popup PlacementTarget parameter. What i am doing wrong? Can i pass clicked button parameter? I know i break mvvm rules when passing view to vm, but how to achieve what i want, when i don't want to define every column in datagrid. Thank you
Your bindings have the DataContext of the DataGridColumnHeader as source which is the value of DataGridColumn.Header. This value in your case is a string and not your expected view model. This is the reason why your bindings doesn't resolve and you get the error message (which is exactly telling you this).
To fix the binding you have to find the next parent element that has the required DataContext, which is I assume the DataGrid:
<Button Name="FilterButton"
Content="[-F-]"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.OpenPopupCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
When checking your code I can see the command which is bound to the Button is toggling the PopupVisible property. I therefore suggest to remove this view related code from the view model and replace the Button with a ToggleButton which binds directly to the Popup.IsOpen:
<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Margin="0,0,0,10"
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}"
HorizontalAlignment="Center" />
<ToggleButton Name="FilterButton"
Content="[-F-]" />
<Popup Name="MyPopup"
StaysOpen="False"
Placement="Right"
IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}"
PlacementTarget="{Binding ElementName=FilterButton}">
<Border Background="White"
BorderBrush="Black"
Padding="5"
BorderThickness="2"
CornerRadius="5">
<StackPanel Orientation="Vertical">
</StackPanel>
</Border>
</Popup>
</StackPanel>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
To get the cell values of all rows contained in a single column where the column header value is the parameter and maps to a property name requires reflection. You need to bind a ListView to the resulting column value collection which uses an ItemTemplate to add a CheckBox to the item. The final version should be as follows:
<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Margin="0,0,0,10"
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RelativeSource={RelativeSource Self}}"
HorizontalAlignment="Center" />
<ToggleButton Name="FilterButton"
Content="[-F-]"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.GenerateFilterViewItemsCommand)}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridColumnHeader}, Path=Content}" />
<Popup Name="MyPopup"
StaysOpen="False"
Placement="Right"
IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}"
PlacementTarget="{Binding ElementName=FilterButton}">
<Border Background="White"
BorderBrush="Black"
Padding="5"
BorderThickness="2"
CornerRadius="5">
<ListView
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.FilterViewItems)}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsIncluded, Mode=OneWayToSource}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.IncludeItemCommand)}"
CommandParameter="{Binding}" />
<ContentPresenter Content="{Binding CellValue}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
</Popup>
</StackPanel>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
To map the filter list to the actual items requires an additional type to hold the information:
Predicate.cs
public class FilterPredicate
{
public FilterPredicate(int rowIndex, object cellValue, object columnKey)
{
this.RowIndex = rowIndex;
this.CellValue = cellValue;
this.ColumnKey = columnKey;
}
public int RowIndex { get; set; }
public object CellValue { get; set; }
public bool IsIncluded { get; set; }
public object ColumnKey { get; set; }
}
You also need to add a collection for the filter view and one for the included item indices to the view model:
UsersViewModel.cs
public class UsersViewModel
{
public UsersViewModel()
{
this.FilterViewItems = new ObservableCollection<FilterPredicate>();
this.IncludedItemsIndex = new Dictionary<object, List<int>>();
}
// The binding source for the Popup filter view
public ObservableCollection<FilterPredicate> FilterViewItems { get; set; }
private Dictionary<object, List<int>> IncludedItemsIndex { get; set; }
public ICommand GenerateFilterViewItemsCommand => new RelayParamCommand((param) =>
{
var columnHeader = param as string;
this.IncludedItemsIndex.Remove(columnHeader);
this.FilterViewItems.Clear();
for (var rowIndex = 0; rowIndex < this.FilteredUserList.Count; rowIndex++)
{
var data = this.FilteredUserList.ElementAt(rowIndex);
var columnValue = data.GetType()
.GetProperty(columnHeader, BindingFlags.Public | BindingFlags.Instance)?
.GetValue(data);
if (columnValue != null)
{
this.FilterViewItems.Add(new FilterPredicate(rowIndex, columnValue, columnValue));
}
}
});
public ICommand IncludeItemCommand => new RelayParamCommand((param) =>
{
var predicate = param as FilterPredicate;
if (predicate.IsIncluded)
{
if (!this.IncludedItemsIndex.TryGetValue(predicate.ColumnKey, out List<int> includedIndices))
{
includedIndices = new List<int>();
this.IncludedItemsIndex.Add(predicate.ColumnKey, includedIndices);
}
includedIndices.Add(predicate.RowIndex);
}
else
{
if (this.IncludedItemsIndex.TryGetValue(predicate.ColumnKey, out List<int> includedIndices))
{
includedIndices.Remove(predicate.RowIndex);
}
}
// Apply the filter
CollectionViewSource.GetDefaultView(this.FilteredUserList).Filter =
data => this.IncludedItemsIndex.Values
.SelectMany(indices => indices)
.Contains(this.FilteredUserList.IndexOf(data as FilteredUser));
});
}
Since you are autogenerating columns, I strongly suspect it is trying to find a OpenPopupCommand in one of the string properties on the items for FilteredUserList.
Try The Following :
Give you'r Page or DataGrid a Name, And change your Binding to
<MenuItem Command="{Binding Path=DataContext.IncludeCommand, Source={x:Reference myPage}}"/>

WPF DataTemplate get Flowdocument

I have two DataTemplate for my EvenementViewModel, one for read only (LectureEvenement) and the second for write (EditeurEvenement).
The template is selected depending of the EvenementViewModel state. These template are used within a TabControl.
From the code behind (Button click), I would like to get the FlowDocument that is in the read only DataTemplate. The goal is to print the FlowDocument.
I need help to get the handle on the FlowDocument that is in the selected tab of the TabControl.
Any suggestion?
There is some part of the XAML code.
<Window.Resources>
<DataTemplate x:Key="EditeurEvenement" DataType="{x:Type local:EvenementViewModel}">
<ScrollViewer Name="Conteneur" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel MaxWidth="1000">
<Border BorderThickness="5" BorderBrush="#55CDD7E1">
<StackPanel Background="#55CDD7E1">
<!-- Centrale -->
<StackPanel DockPanel.Dock="Top" Margin="0,0,0,5" HorizontalAlignment="Left">
<TextBlock VerticalAlignment="Center"><Run Text="Centrale :" FontWeight="Bold"/></TextBlock>
<ComboBox MinWidth="160" ItemsSource="{Binding ListeCentrale}" DisplayMemberPath="Nom" SelectedItem="{Binding Centrale}"/>
</StackPanel>
...
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</DataTemplate>
<DataTemplate x:Key="LectureEvenement" DataType="{x:Type local:EvenementViewModel}">
<FlowDocumentScrollViewer VerticalScrollBarVisibility="Auto">
<FlowDocument FontFamily="Sergoe UI" FontSize="12px" Name="FdEvenement">
<Paragraph>
<!-- Centrale -->
<TextBlock Margin="0,0,0,5"><Run Text="Centrale : " FontWeight="Bold"/><Run Text="{Binding Centrale.Nom, Mode=OneWay}"/></TextBlock>
<LineBreak/>
...
</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
</DataTemplate>
<DataTemplate DataType="{x:Type local:EvenementViewModel}">
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource LectureEvenement}"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding EstModifiable}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource EditeurEvenement}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</Window.Resources>
<TabControl x:Name="TcEvenements" ItemsSource="{Binding ListeOngletOuvert}" SelectedItem="{Binding OngletSelectionne}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding TitreTab}" Margin="0,0,5,0" VerticalAlignment="Center"/>
<Button CommandParameter="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Command="{Binding LancerFermer}">
<Image Source="Icone/icons8-delete-23.png" Width="15"/>
</Button>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
The FlowDocumentScrollViewer element is a visual child of the TabControl whenever your template is applied. You can then get a reference to it using the VisualTreeHelper class:
private void Button_Click(object sender, RoutedEventArgs e)
{
FlowDocumentScrollViewer fdsv = FindVisualChild<FlowDocumentScrollViewer>(TcEvenements);
if (fdsv != null)
{
FlowDocument fd = fdsv.Document;
//...
}
}
private T FindVisualChild<T>(Visual visual) where T : Visual
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
Visual child = (Visual)VisualTreeHelper.GetChild(visual, i);
if (child != null)
{
T correctlyTyped = child as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
T descendent = FindVisualChild<T>(child);
if (descendent != null)
{
return descendent;
}
}
}
return null;
}

XAML => I can't access Datagrid parent from Group child

I like to Binding the datagrid parent from Behavior (pnlDgSubFooter) placed in DataGrid/GroupStyle/ContainerStyle/Style[GroupItem]/Setter[template]/ControlTemplate/Expander/Header/DockPanel/StackPanel
But the Binding can't leave of DockPanel.
in ElementName, there is only "btnExpandAll" and himself.
In Behavior, the LeDataGrid property return alway null (except if i bind on . or btnExpandAll)
<DataGrid Grid.Row="0" x:Name="dgMain" AutoGenerateColumns="False" SelectionUnit="FullRow" LoadingRow="dgMain_LoadingRow" MouseDown="dgMain_MouseDown" Sorting="dgMain_Sorting"
CanUserReorderColumns="False" CanUserResizeColumns="True" CanUserResizeRows="False" CanUserSortColumns="True" CanUserAddRows="False"
Style="{StaticResource dg}" RowStyle="{StaticResource dgRow}" CellStyle="{StaticResource dgCell}" ColumnHeaderStyle="{StaticResource dgColHeader}" RowHeaderStyle="{StaticResource dgRowHeader}"
ItemsSource="{Binding NotifyOnSourceUpdated=True, Source={StaticResource cvsElmts}}" HorizontalAlignment="Left" >
<!--DataGrid.DataContext><Binding Source="{StaticResource tblUsers}"/></DataGrid.DataContext-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<mvvm:EventToCommand Command="{Binding SendCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=dgMain}" PassEventArgsToCommand="False"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.GroupStyle>
<GroupStyle>
<!-- Style for groups under the top level. -->
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="{Binding DataContext.ExpandedAll, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" BorderThickness="1,1,1,5">
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsBottomLevel}" Value="True">
<Setter Property="Margin" Value="8,0,0,0" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<Expander.Header>
<DockPanel>
<Button Name="btnExpandAll" Command="{Binding DataContext.ExpandedAllCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Click="btnExpandAll_Click" ToolTip="{x:Static resx:resMain.lblExpandAll}" BorderThickness="0">
<TextBlock Grid.Column="0" Text="" FontFamily="{StaticResource FntSymbol}" Foreground="{StaticResource scbBlack}" FontSize="12" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Button>
<TextBlock FontWeight="Bold" Text="" Margin="5,0,0,0"><Run Text="{Binding Path=Name, Mode=OneWay}" /><Run Text=" ("/><Run Text="{Binding Path=ItemCount, Mode=OneWay}" /><Run Text=" éléments)."/></TextBlock>
<StackPanel Name="pnlDgSubFooter" HorizontalAlignment="Left" Orientation="Horizontal" >
<i:Interaction.Behaviors>
<Classes:BehaviorWithCommand LeDataGrid="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" LeGroupe="{Binding .}" />
</i:Interaction.Behaviors>
</StackPanel>
</DockPanel>
</Expander.Header>
<Expander.Content>
<Border BorderThickness="1" BorderBrush="{StaticResource scbGrey3}">
<ItemsPresenter />
</Border>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
....
The Behavior:
public class BehaviorWithCommand : Behavior<FrameworkElement> { // StackPanel
public static readonly DependencyProperty LeDataGridProperty = DependencyProperty.Register(nameof(LeDataGrid), typeof(object), typeof(BehaviorWithCommand), new PropertyMetadata(null));
public static readonly DependencyProperty LeGroupeProperty = DependencyProperty.Register(nameof(LeGroupe), typeof(object), typeof(BehaviorWithCommand), new PropertyMetadata(null));
public object LeDataGrid {
get { return (object)GetValue(LeDataGridProperty); }
set { SetValue(LeDataGridProperty, value); }
}
public object LeGroupe {
get { return (object)GetValue(LeGroupeProperty); }
set { SetValue(LeGroupeProperty, value); }
}
protected override void OnAttached() {
base.OnAttached();
((Panel)AssociatedObject).Children.Clear();
var dg = new DataGrid();
dg.Columns.Add(new DataGridTextColumn());
dg.Columns.Add(new DataGridTextColumn());
dg.Columns.Add(new DataGridTextColumn());
//var dg = (DataGrid)LeDataGrid;
foreach (var item in dg.Columns) {
var g = new Grid() { MinWidth = 10 };
g.SetBinding(Grid.WidthProperty, new System.Windows.Data.Binding("ActualWidth") { Source = item }); // dhHeadName DataGridColumn
var t = new TextBox() { Margin = new Thickness(1, 0, 1, 0), Background = new SolidColorBrush(Color.FromRgb(200, 200, 200)), FontWeight = FontWeights.Bold, FontSize = 9, IsReadOnly = true };
t.Text = "Coucou";
g.Children.Add(t);
((Panel)AssociatedObject).Children.Add(g);
}
}
protected override void OnDetaching() {
base.OnDetaching();
}
}
This is working:
<StackPanel Grid.ColumnSpan="2" Grid.Row="1" Name="pnlDgSubFooter" HorizontalAlignment="Left" Orientation="Horizontal" Margin="-20, 0, 0, 0">
<i:Interaction.Behaviors>
<Classes:BehaviorWithCommand LeDataGrid="{Binding ., ElementName=dgMain}" LeGroupe="{Binding .}" />
</i:Interaction.Behaviors>
</StackPanel>
It's just that designer don't resolve it.
Regards.

How to get the checked items from combobox in wpf?

I have a combobox with checkbox and items, How do i select the particular item or multi item when checked in combobox.. Need help.. Below i tried code..
<UserControl.Resources>
<DataTemplate x:Key="cmbIndex">
<CheckBox IsChecked="{Binding Path=IsSelected, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Tag="{RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}"
Content="{Binding}"
Click="CheckBox_Click">
</CheckBox>
</DataTemplate>
<CollectionViewSource x:Key="coll" Source="{Binding CMDCollection}"/>
<UserControl.Resources>
<ComboBox Grid.Row="0"
HorizontalAlignment="Left" Margin="80,0,0,0"
SelectedItem="{Binding T3Command}"
Height="20" VerticalAlignment="Center" Width="60"
FontFamily="Calibri" FontSize="12">
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem>
<CheckBox x:Name="all">Select All</CheckBox>
</ComboBoxItem>
<CollectionContainer Collection="{Binding Source={StaticResource coll}}"/>
</CompositeCollection>
</ComboBox.ItemsSource>
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="chkTask" Content="{Binding}" IsChecked="{Binding ElementName=all, Path=IsChecked, Mode=OneWay}"></CheckBox>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemTemplate" Value="{StaticResource cmbIndex}"/>
</Style>
</ComboBox.Style>
</ComboBox>
1) Add a Bool type IsSelected property in your model. and bind that property to IsChecked property of Checkbox in Template.
2) Bind that property to ComboBoxItem's IsSelected in ItemContainerStyle.
you might want to come up with other solution to make Check All working(like converters).
this will work.

Prevent updation to ObservableCollection on Error

Is there a way to prevent updating to ObservableCollection list when there is an validation error in xaml? I am using "Binding" in the xaml
my xaml
<ControlTemplate x:Key="ErrorTemplate">
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<DataGrid Name="grid" HorizontalAlignment="Stretch" ItemsSource="{Binding mMngModelList}" Margin="0,0,0,50" VerticalAlignment="Stretch" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Type">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Type}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Type}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Range Left">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding RangeLeft,ValidatesOnDataErrors=True, NotifyOnValidationError=True, ValidatesOnExceptions=True}" Validation.ErrorTemplate="{StaticResource ErrorTemplate}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding RangeLeft, ValidatesOnDataErrors=True, NotifyOnValidationError=True, ValidatesOnExceptions=True}" Validation.ErrorTemplate="{StaticResource ErrorTemplate}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
code behind is
public string this[string columnName]
{
get
{
var result = string.Empty;
switch (columnName)
{
case "RangeLeft":
if (RangeLeft == 0)
{
result = "RangeLeft should be greater than zero";
}
break;
}
return result;
}
}
public string Error
{
get
{
StringBuilder error = new StringBuilder();
// iterate over all of the properties
// of this object - aggregating any validation errors
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(this);
foreach (PropertyDescriptor prop in props)
{
String propertyError = this[prop.Name];
if (propertyError != string.Empty)
{
error.Append((error.Length != 0 ? ", " : "") + propertyError);
}
}
return error.Length == 0 ? null : error.ToString();
}
}
The above code is to display error in the tooltip and the cell goes red when there is a validation error. The problem is that the collection is updated with invalid values. Is there a way to prevent updation to the collection till all validation is resolved by the user?
you can solve your problem by clone the object that is enter in the editing mode, when there is validation return cloning object and make refresh on DataGrid Items

Resources