Prevent updation to ObservableCollection on Error - wpf

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

Related

Why is my DataGrid not showing the values from my list?

So I have 2 DataGrids (because I haven't found a better way to do it the way I want it) and I have the following code in the .cs:
public MainWindow()
{
InitializeComponent();
List<User> users = new List<User>();
users.Add(new User() { Id = 1, Name = "John Doe", Birthday = new DateTime(1971, 7, 23) });
users.Add(new User() { Id = 2, Name = "Jane Doe", Birthday = new DateTime(1974, 1, 17) });
users.Add(new User() { Id = 3, Name = "Sammy Doe", Birthday = new DateTime(1991, 9, 2) });
gridd.ItemsSource = users;
string[] items = new string[]
{
"Asdf",
"qwer",
"sdfg",
"wert",
};
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
I only want to output this data in my DataGrid for testing purposes, but for some reason it doesn't work. Here is my xaml code:
<Grid>
<DataGrid HorizontalAlignment="Left" Height="298.638" Margin="93.195,102.655,0,0"
VerticalAlignment="Top" Width="507.242">
<DataGrid.Resources>
<!--Design kopfzeile-->
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="Gray"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0,0,1,0" />
<Setter Property="BorderBrush" Value="White"/>
<Setter Property="Padding" Value="5 0 0 0"/>
</Style>
<!--Deaktivieren Des rowheader-->
<Style TargetType="{x:Type DataGridRowHeader}">
<Setter Property="Background" Value="Transparent"/>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Width="*" Binding="{x:Null}" ClipboardContentBinding="{x:Null}" Header="Artikelnummer" FontSize="10" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Binding="{x:Null}" ClipboardContentBinding="{x:Null}" FontSize="10" Header="Artikelnummer" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Binding="{x:Null}" ClipboardContentBinding="{x:Null}" Header="Matchcode" IsReadOnly="True"/>
<DataGridTextColumn Width="*" Binding="{x:Null}" ClipboardContentBinding="{x:Null}" Header="Hersteller" IsReadOnly="True"/>
<DataGridCheckBoxColumn Width="*" Binding="{x:Null}" ClipboardContentBinding="{x:Null}" Header="CheckBox" />
</DataGrid.Columns>
</DataGrid>
<!--!!!!!!Haupt DataGrid !!!!!-->
<DataGrid AutoGenerateColumns="False" Height="282.293" HorizontalAlignment="Left" Margin="93.195,119,0,0" Name="gridd" VerticalAlignment="Top" Width="507.242" >
<DataGrid.Columns >
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="88.048">
<TextBox Name="IDSearcBox" Width="88"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Name="NameSearchBox" Width="88"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Name="NameSearchBox" Width="88"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Name="NameSearchBox" Width="88"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Name="NameSearchBox" Width="88"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
It shows me the number of lines as I have saved in the list:
But it does not show the values for it and especially since it is formatted in a weird way. I would be happy if someone sees my mistake here.
You use a DataGridTemplateColumn but you do not supply data templates for displaying and editing.
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Name="NameSearchBox" Width="88"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Alternatively, just use already built-in column types like DataGridTextColumn. In case you do not need custom data templates to display your data, there is no need for a template column.
<DataGridTextColumn Width="*" Binding="{Binding Id}">
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="88.048">
<TextBox Name="IDSearcBox" Width="88"/>
</StackPanel>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
</DataGridTextColumn>

Getting ContextMenu on DataGrid in WPF/MVVM

UPDATE!!!
I've discovered that even this doesn't work when I put it in the DataGrid:
<DataGrid.ContextMenu>
<ContextMenu >
<MenuItem Header="Add Divider" Click="MenuItem_Click" />
</ContextMenu>
</DataGrid.ContextMenu>
This definitely works in a dummy project set up from scratch. So I don't think I'm having binding problems or datacontext problems.... I'm not getting that far. I suspect that some other part of the program is intercepting and handling the context menu (or right click). I didn't write the original code and it's a large code base. Can someone give me an idea what I should be looking for? What would stop the ContextMenu on the DataGrid from being called. Note that the DataGrid is in a ScrollViewer and StackPanel and the whole control is part of a larger window. In fact, it is one of many tabs.
-END UPDATE
I'm pulling my hair out trying to get a context menu on my DataGrid in my WPF/MVVM project. I post the XAML and ViewModel below. I would like to be able to right click on a row and call a command in the ViewModel that would change one of the column entries for that row.
The XAML (I call out what I think are the key points below)
<UserControl x:Class="Sears.UserInterface.Views.Technician.Alerts.AlertsLogView"
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:Sears.UserInterface.Views.Technician.Alerts"
xmlns:converters="clr-namespace:Common.Controls.Converters;assembly=Common.Controls"
mc:Ignorable="d"
d:DesignHeight="800"
d:DesignWidth="1200">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" />
</UserControl.Resources>
<StackPanel Orientation="Horizontal">
<ScrollViewer>
<DataGrid Width="1000"
Margin="0"
HorizontalAlignment="Left"
AutoGenerateColumns="False"
Background="Transparent"
DataContext="{Binding}"
HeadersVisibility="Column"
ItemsSource="{Binding Alerts}"
SelectedItem="{Binding SelectedItemProperty, Mode=TwoWay}"
RowBackground="Transparent"
RowHeight="30"
Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}"
>
**<DataGrid.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Command="{Binding CloseAlertCommand}" Header="Close Alert"/>
</ContextMenu>
</DataGrid.ContextMenu>**
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Foreground" Value="Black" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="FontWeight" Value="Bold" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="*" Header="Id">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Id}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="TimeStamp">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding AlertTime}" >
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Severity">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Severity}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="AlertText">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding AlertText}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Details">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Details}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Active">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Active}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Acknowledged">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Acknowledged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Reported">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Reported}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Status">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Status}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Resolution">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Resolution}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header="Category">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
Padding="5"
Text="{Binding Category}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
</StackPanel>
Notice the DataGrid.ContextMenu part. I've searched StackOverflow and tried various ideas to get the contextmenu showing up. Nothing. This particular example uses "Tag". I also tried putting the contextmenu in the resources sections as:
<ContextMenu x:Key="DataRowContextMenu" DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}}">
<MenuItem x:Name="RowContMenuTransfer" Header="Close Alert" Command="{Binding DataContext.CloseAlertCommand}" CommandParameter="{Binding}" >
</MenuItem>
</ContextMenu>
The "Tag" way of doing it results in no binding errors. You just right click and nothing! Other ways (which I can post) gave binding errors in the output window. Possibly of note is the fact that the DataGrid is inside a ScrollViewer and StackPanel.
Here is the ViewModel
public class AlertsLogViewModel : ViewModelBase, IAlertsLogViewModel, IDisposable
{
private IAlertManager _model;
private readonly object _lock = new object();
private ICommand _closeAlertCommand;
public AlertsLogViewModel(IAlertManager model) : base("AlertsLogViewModel")
{
Alerts = new ObservableCollection<IAlert>();
_model = model ?? throw new ArgumentNullException("model");
_model.AlertStatusUpdated += _model_AlertStatusUpdated;
UpdateAlerts();
}
public IAlert SelectedItemProperty { get; set; }
public ICommand CloseAlertCommand
{
get { return _closeAlertCommand ?? (_closeAlertCommand = new DelegateCommand(CloseAlert)); }
}
private void CloseAlert()
{
_model.CloseAlert(SelectedItemProperty.SystemErrorGuid);
}
public ObservableCollection<IAlert> Alerts { get; private set; }
private void _model_AlertStatusUpdated(object sender, DataAccess.AlertStatusEventArgs e)
{
UpdateAlerts();
}
private void UpdateAlerts()
{
RunOnDispatcher(() =>
{
lock (_lock)
{
var modelAlerts = _model.GetAlerts().OrderBy(p => (int)p.Status).ThenByDescending(p => p.AlertTime);
Alerts.Clear();
if (modelAlerts != null)
{
foreach (var alert in modelAlerts)
{
Alerts.Add(alert);
}
}
}
}
);
NotifyPropertyChanged("Alerts");
}
public void Dispose()
{
if (_model != null)
_model.AlertStatusUpdated -= _model_AlertStatusUpdated;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Note the CloseAlertCommand and SelectedItemProperty. I do see that SelectedItemProperty is being called when I left click, but not right click. CloseAlert is never called.
Finally, in case it's relevant, let me mention that the AlertsLogView is inside a Technicial.xaml page and it is here that it's bound to the ViewModel:
<TabItem Header="Alerts"
PreviewMouseDown="OnPreviewMouseDown"
PreviewTouchDown="OnPreviewTouchDown">
<alertlog:AlertsLogView Margin="5"
DataContext="{Binding AlertsLogViewModel}" />
</TabItem>
Many thanks for any hints, references, pointers or solutions!
-Dave
So, as I have already written in comments, the binding works. I have thought, that you get ContextMenu invoked, but you write you didn't. MouseRightButtonClick can be intercepted by several things:
Implicit or explicit styles for grid or it's parents(If explicit -
comment it out, if implicit - set your own dummy style)
Behaviors for grid or it's parents(comment it out)
Code behind(check, whether there are event handlers in grid or it's parents)
Make it consistently and you will find what is responsible for the issue.

How to stretch checkbox item the full width of the combobox

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.

Datagrid Column with DropDown List To Be Shown on One Mouse Click

I have a WPF datagrid as shown in the code below. The Trade column of the grid has a dropdown. When a user clicks on the cell it selects the cell. They then have to click on the cell again, to be taken to the cell edit mode and show the dropdown. They then have to click on the cell a third time in order to see the dropdown's list.
I wanted to know if anyone can either show me how to dropdown the combo's list on the first click instead of 3 clicks or perhaps point me in the direction of what I should be looking to do in order to enable this. Thanks in advance.
<DataGrid SelectionMode="Single" Grid.Row="0" Name="grd_S_TML" CanUserAddRows="False" TabIndex="41" MinHeight="{Binding Grd_S_TMLHeight}" ItemsSource="{Binding GrdSTmlCollection}" SelectedIndex="{Binding GrdSTmlSelectedIndex}" SelectedItem="{Binding GrdSTmlSelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="false"
behaviors:DragManagerExtended.DragOverCommand="{Binding GrdStmlDragOverCommand}" behaviors:DragManagerExtended.DragDropCommand="{Binding GrdStmlDragDropCommand}" behaviors:DragManagerExtended.IsDropTarget="True" ContextMenu="{StaticResource mnuMGridPopUp}" CellStyle="{StaticResource OverrideGridCellHighlight}"
behaviors:DragManagerExtended.IsNeedToBeDisposed="{Binding IsFormClosed}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding SequenceNo}" Width="0" Visibility="Collapsed" IsReadOnly="True" />
<DataGridTextColumn Header="Status" Binding="{Binding RowStatus}" Width="0" Visibility="Collapsed" IsReadOnly="True" />
<DataGridTemplateColumn Header="Trade" Width="201">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock HorizontalAlignment="Left" Text="{Binding Path=TradeSelectedItem.Text}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox x:Name="cboTrades" ItemsSource="{Binding TradeCollection}" DisplayMemberPath="Text" SelectedValuePath="ID" SelectedItem="{Binding TradeSelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<behaviors:KeyPressedBehavior>
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},Path=DataContext.RoleComboBoxKeyPressEvent}" />
</behaviors:KeyPressedBehavior>
</i:Interaction.Triggers>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Pay Rate" Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding PayRate, StringFormat={}{0:0.00}}" Style="{StaticResource TextBlockCenterAlligned}" TextAlignment="Right" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=PayRate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MaxLength="6" TextAlignment="Right" HorizontalContentAlignment="Right" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Qty" Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Quantity, StringFormat={}{0:0.00}}" Style="{StaticResource TextBlockCenterAlligned}" TextAlignment="Right" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Quantity, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" MaxLength="6" TextAlignment="Right" HorizontalContentAlignment="Right" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Rate (p/hr)" Binding="{Binding Path=RateHR, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat={}{0:0.00}}" Width="60" IsReadOnly="True" ElementStyle="{StaticResource RightAlignDataGridTextColumnStyle}" />
<DataGridTextColumn Header="Value (£)" Binding="{Binding ItemValue, StringFormat={}{0:0.00}}" Width="60" IsReadOnly="True" ElementStyle="{StaticResource RightAlignDataGridTextColumnStyle}" />
</DataGrid.Columns>
<i:Interaction.Behaviors>
<behaviors:DataGridColumnIndexAndCellPositionBehavior ColumnIndex="{Binding Source={StaticResource vmOrderPricing},Path=StmlColumnIndex,Mode=TwoWay}" />
<behaviors:DataGridRowClickBehavior RowClickCommand="{Binding GrdStmlMouseDownCommand}" />
<behaviors:DataGridCellEditEndingBehavior CellEditEndingCommand="{Binding GrdStmlAfterColUpdateCommand}" />
<behaviors:DataGridBeginningEditBehavior BeginningEditCommand="{Binding GrdStmlBeforeColEditCommand}" />
</i:Interaction.Behaviors>
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding GrdStmlLostFocusCommand}" />
</i:EventTrigger>
<behaviors:KeyPressedBehavior>
<i:InvokeCommandAction Command="{Binding GrdStmlKeyPressCommand}" />
</behaviors:KeyPressedBehavior>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding GrdStmlRowColChangeCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RowStatus}" Value="Edit"></Condition>
<Condition Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=DataContext.LoadSalesOnly}" Value="false"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="{StaticResource ROW_STYLE_HIGHLIGHT_CHANGE}" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RowStatus}" Value="Add"></Condition>
<Condition Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=DataContext.LoadSalesOnly}" Value="false"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="{StaticResource ROW_STYLE_HIGHLIGHT_NEW}" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RowStatus}" Value="Delete"></Condition>
<Condition Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=DataContext.LoadSalesOnly}" Value="false"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="{StaticResource ROW_STYLE_HIGHLIGHT_DELETE}" />
</MultiDataTrigger>
<DataTrigger Binding="{Binding RowStatus}" Value="View">
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
Ok i've solved this. Thought I'd post it here for others who might stumble upon this problem.
I created a behavior on the following dependency property.
public static readonly DependencyProperty IsAllowSingleClickEditProperty = DependencyProperty.RegisterAttached("IsAllowSingleClickEdit", typeof(bool), typeof(DataGridCellSingleClickEditDependency), new PropertyMetadata(false, IsAllowSingleClickEditChanged));
...in the behavior I linked this event to this method...
private static void IsAllowSingleClickEditChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DataGridCell dataGridCell = sender as DataGridCell;
if (dataGridCell != null)
{
if (e.NewValue.Equals(true))
{
dataGridCell.GotFocus += DataGridCellGotFocusHandler;
}
else
{
dataGridCell.GotFocus -= DataGridCellGotFocusHandler;
}
}
}
... and then finally this code in the DataGridCellGotFocusHandler...
DataGridCell cell = sender as DataGridCell;
if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
{
if (!cell.IsFocused)
{
cell.Focus();
}
DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
if (dataGrid != null)
{
dataGrid.BeginEdit(e);
if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
{
if (!cell.IsSelected)
{
cell.IsSelected = true;
}
}
else
{
DataGridRow row = FindVisualParent<DataGridRow>(cell);
if (row != null && !row.IsSelected)
{
row.IsSelected = true;
}
}
Control control = GetFirstChildByType<Control>(e.OriginalSource as DataGridCell);
if (control != null)
{
control.Focus();
TextBox txt = control as TextBox;
if (txt != null)
{
txt.Select(0,txt.Text.Length);
}
}
}
}
Instead of rolling your own combo box in a DataGridTemplate column with a combobox.... It might be worthwhile to look into a DataGridComboBoxColumn?

Wpf - Issue with visibility of ToggleButtons inside a cell of a datagrid

I am currently working on a WPF application which contains a datagrid with 3 columns containing ToggleButtons whose code is as follows
<DataGridTemplateColumn Header="Closed" Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton>
<ToggleButton.Content>
...
</ToggleButton.Content>
</ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Checked" Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton Visibility="{Binding Path=IsClosedProperty, Converter={StaticResource toggleButtonVisibilityConverter}}">
<ToggleButton.Content>
...
</ToggleButton.Content>
</ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Active" Width="60">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton Visibility="{Binding Path=IsCheckedProperty, Converter={StaticResource toggleButtonVisibilityConverter}}">
<ToggleButton.Content>
...
</ToggleButton.Content>
</ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
The code of the toggleButtonVisibilityConverter is as follows
public class ToggleButtonVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value is bool && ((bool)value) ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The application should work such that when ToggleButton of a row in the Closed column is clicked, the ToggleButton in the Checked column of that row becomes visible(i.e, toggleButtonVisibilityConverter is called). Also when ToggleButton of a row in the Checked column is clicked, the ToggleButton in the Active column of that row becomes visible. And when the respective ToggleButtons are unclicked they should become hidden in the same hierarchy.
The IsClosedProperty and the IsCheckedProperty are boolean properties of ViewModel which are set true when Closed and Checked toggleButtons are clicked respectively.
Class to hold data
public class GridToggleButtonItem
{
private bool _isClosedProperty;
public bool IsClosedProperty
{
get { return _isClosedProperty; }
set { _isClosedProperty = value; }
}
private bool _isCheckedProperty;
public bool IsCheckedProperty
{
get { return _isCheckedProperty; }
set { _isCheckedProperty = value; }
}
}
ViewModel that has items collection to be bound to datagrid
public class GridToggleButtonViewModel
{
public List<GridToggleButtonItem> Items { get; set; }
public GridToggleButtonViewModel()
{
Items = new List<GridToggleButtonItem>()
{
new GridToggleButtonItem() { IsCheckedProperty = false, IsClosedProperty = false},
new GridToggleButtonItem() { IsCheckedProperty = false, IsClosedProperty = false},
new GridToggleButtonItem() { IsCheckedProperty = false, IsClosedProperty = false},
new GridToggleButtonItem() { IsCheckedProperty = false, IsClosedProperty = false},
new GridToggleButtonItem() { IsCheckedProperty = false, IsClosedProperty = false}
};
}
}
XAML code that binds to viewmodel
<Window x:Class="StackOverFlowQ.GridToggleButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StackOverFlowQ"
Title="GridToggleButton" Height="500" Width="500">
<Window.DataContext>
<local:GridToggleButtonViewModel></local:GridToggleButtonViewModel>
</Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Margin" Value="3"></Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="FontWeight" Value="Bold"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<DataGrid ItemsSource="{Binding Path=Items}" AutoGenerateColumns="False" SelectionMode="Single" SelectionUnit="Cell" CanUserAddRows="False" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Closed" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton Content="Closed" IsChecked="{Binding Path=IsClosedProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Checked" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton Content="Checked" Visibility="{Binding Path=IsClosedProperty, Converter={StaticResource ResourceKey=BooleanToVisibilityConverter}}" IsChecked="{Binding Path=IsCheckedProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Active" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton Content="Active" Visibility="{Binding Path=IsCheckedProperty, Converter={StaticResource ResourceKey=BooleanToVisibilityConverter}}"></ToggleButton>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<Grid>
<Grid.Resources>
<FrameworkElement x:Key="ProxyElement" DataContext="{Binding DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}}" />
</Grid.Resources>
<ContentControl Content="{StaticResource ProxyElement}" Visibility="Collapsed" />
<DataGrid
ItemsSource="{Binding SomeData}">
<DataGrid.Columns>
<DataGridTemplateColumn IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image MaxWidth="16"
MaxHeight="16"
Source="/abc.png" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn MinWidth="80" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Foreground="Black" Text="{Binding Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn MinWidth="64"
Binding="{Binding Category}"
Foreground="Black"
IsReadOnly="True"
Visibility="{Binding Path=DataContext.IsCategoryColumnVisible,
Source={StaticResource ProxyElement},
Converter={StaticResource BooleanVisibilityConverter}}" />
<DataGridTextColumn MinWidth="64"
Binding="{Binding Size}"
Foreground="Black"
IsReadOnly="True"
Visibility="{Binding Path=DataContext.IsSizeColumnVisible,
Source={StaticResource ProxyElement},
Converter={StaticResource BooleanVisibilityConverter}}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
DataGridColumns are not part of Visual Tree, so your binding will not work. Here is my solution.
Create a proxy object and bind its DataContext to correct object which should be in DataContext(your ViewModel).
Put it in a ContentControl and make it hidden.
Use your proxy object as Source of your binding.

Resources