Checkbox ListView WPF C# - c#

I will start developing for WPF and I have a doubt.
I created a ListView with the Binding property to the next ExtComandaDTO the object. The property in "seleciona" has relationship with a checkbox, but I have following problem.
When I click the checkbox it calls the normal event, but when I change the value of "seleciona" at runtime checkbox in my listview is selected but does not call the event check.
There is missing from Listview to be called the event some attribute?
<ListView x:Name="LvwComanda" Grid.Column="0"
Background="{x:Null}"
Margin="40,36,40,40"
SelectedItem="{Binding SelectedExtComanda}"
ItemsSource="{Binding ObsExtComanda, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.RowSpan="2" >
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Finaliza Comanda" Checked="LvwComandaRowFinalizaComanda_Click" Unchecked="LvwComandaRowFinalizaComanda_Click"></MenuItem>
</ContextMenu>
</ListView.ContextMenu>
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding finaliza_pendente}" Value="true">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding finalizada}" Value="true">
<Setter Property="Foreground" Value="DarkViolet" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Resources>
<ListView.View>
<GridView >
<GridViewColumn Width="30">
<GridViewColumn.CellTemplate>
<DataTemplate >
<CheckBox Name="ChkComanda" IsChecked="{Binding seleciona.IsChecked, Mode=TwoWay}" Checked="Checked_LvwComandaRow" Unchecked="Unchecked_LvwComandaRow" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="Auto" Header="Comanda" DisplayMemberBinding="{Binding nr_comanda}"/>
<GridViewColumn Width="Auto" Header="Taxa Servico" DisplayMemberBinding="{Binding taxa_servico}" />
<GridViewColumn Width="Auto" Header="Finalizada" DisplayMemberBinding="{Binding finalizada, Converter={StaticResource ReplaceConvertSimNao}}" />
<GridViewColumn Width="Auto" Header="Observacao" DisplayMemberBinding="{Binding observacao}"/>
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel>
<DockPanel.ContextMenu>
<ContextMenu Loaded="LvwComandaHeaderContextMenu_Loaded">
<MenuItem Header="Libera Mesa" Checked="LvwComandaHeaderLiberaMesa_Click" Unchecked="LvwComandaHeaderLiberaMesa_Click" />
</ContextMenu>
</DockPanel.ContextMenu>
<CheckBox x:Name="HeaderCheckBox" Checked="Checked_LvwComandaHeader" Unchecked="Unchecked_LvwComandaHeader">
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" Text="{Binding Name, Converter={StaticResource ReplaceConvertMesaId}}" Margin="5,0,0,0"/>
<TextBlock Width="Auto" Text=" " />
<TextBlock FontWeight="Bold" Width="Auto" Text="{Binding Name, Converter={StaticResource ReplaceConvertMesaGrupo}}" />
<TextBlock Text=" ("/>
<TextBlock Text="{Binding ItemCount, Converter={StaticResource ReplaceConvertComanda}}"/>
<TextBlock Text=")"/>
</StackPanel>
</CheckBox>
</DockPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
#region Event List Row Comanda
private void Checked_LvwComandaRow(object sender, RoutedEventArgs e)
{
this.Handle_LvwComandaRow((CheckBox)sender, true);
}
private void Unchecked_LvwComandaRow(object sender, RoutedEventArgs e)
{
this.Handle_LvwComandaRow((CheckBox)sender, false);
}
private void Handle_LvwComandaRow(CheckBox sender, bool check)
{
if (sender.DataContext is ExtComandaDTO)
{
var row = (ExtComandaDTO)sender.DataContext;
if (check)
{
ObsExtComanda.FindAll(c => c.seleciona && c.id_mesa != row.id_mesa).ForEach(c => c.seleciona = false);
}
bool bolComandaSelected = ObsExtComanda.Exists(c => c.seleciona);
BtPagamento.IsEnabled = bolComandaSelected;
BtImprimir.IsEnabled = bolComandaSelected;
this.PrepareObsPedido(check, row);
this.PrepareObsComandaPagto(check, row);
}
}
public class ExtComandaDTO : ComandaDTO, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
private Boolean _seleciona;
private Boolean _finaliza_pendente;
public Boolean seleciona
{
get { return _seleciona; }
set { _seleciona = value; OnPropertyChanged("seleciona"); }
}
public new Boolean finaliza_pendente
{
get { return _finaliza_pendente; }
set { _finaliza_pendente = value; OnPropertyChanged("finaliza_pendente"); }
}
}

Checked and Unchecked are events fired by the UI (not a set).
Handle that stuff in the set if you want to catch changes from code.

Related

Expand Header binding don't show the value properly (WPF/C#)

I'm using data binding to show some data in a listview. I'm using headers to sumarize my data and I want to show a text extracted from my itemsource but doesn't worked. There is some mistake in this portion of code? How can I provide the correct datasource to my wpf?
In Main.xml I fill my list with StructLog.cs class, and on wpf I show the data from each item of the list. The others values work normally, and the header are created, just the text on header expander don't appear.
Main.xml
List<StructLog> all = new List<StructLog>();
foreach (ObservableCollection<StructLog> res in Patterns.Results)
{
foreach (StructLog r in res)
{
all.Add(r);
}
}
lstResults.ItemsSource = all;
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(all);
PropertyGroupDescription groupDescription = new PropertyGroupDescription("Pattern");
view.GroupDescriptions.Add(groupDescription);
StructLog.cs
public class StructLog
{
public int LineNumber{ get; set;}
public string LineLog{ get; set;}
public DateTime Time{ get; set;}
public string Source{ get; set;}
public string Type{ get; set;}
public string Pattern{ get; set;}
public StructLog(StructLine s,string patternName)
{
this.LineNumber = s.LineNumber;
this.LineLog = s.LineLog;
this.Time = s.Time;
this.Source = s.Source;
this.Type = s.Type;
this.Pattern = patternName;
}
}
Window.xaml
<ListView Name="lstResults" Grid.Row="1" IsEnabled="True" Grid.RowSpan="4" DataContext="Results" Grid.ColumnSpan="5" Margin="5,5">
<ListView.View>
<GridView>
<GridViewColumn Header="Linha" Width="Auto" DisplayMemberBinding="{Binding LineNumber}" />
<GridViewColumn Header="Fonte" Width="Auto" DisplayMemberBinding="{Binding Source}" />
<GridViewColumn Header="Data" Width="Auto" DisplayMemberBinding="{Binding Time}" />
<GridViewColumn Header="Log" Width="Auto" DisplayMemberBinding="{Binding LineLog}" />
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Teste:" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding Pattern}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
In order to display a value in the group header, grouped items will in the Items property of the GroupItem class. So you can do binding like Text ="{Binding Items[0].Pattern }". This will bind the value of the first item in the group, since all the values in the group will be similar as the values are grouped by Pattern property.
Try this.
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Teste:" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding Items[0].Pattern}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>

Wpf ScrollIntoView not working first time

I have a ListView include Expander, and I assign a SelectedIndex, then call ScrollIntoView to SelectedItem position. (It can auto expand.)
lv_SelectionChanged was invoke every times.
private void lv_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (Expander exp in FindVisualChildren<Expander>(lv.lv))
{
var a = (exp.Header as StackPanel).Children[0] as TextBlock;
if (a.Text.Equals((lv.lv.SelectedItem as User).group))
exp.IsExpanded = true;
}
lv.ScrollIntoView(lv.SelectedItem);
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
But, it was not working at first time. (If it's a simple ListView, it work!)
I have no idea.
Thx.
xaml
<UserControl.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
<Style x:Key="GroupHeaderStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!--<Expander IsExpanded="{Binding Mode=TwoWay, Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListViewItem}, Mode=FindAncestor}}" MouseRightButtonDown="Expander_MouseRightButtonDown">-->
<Expander IsExpanded="False" MouseRightButtonDown="Expander_MouseRightButtonDown">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Name="mi_ExpandAll" Header="Hide Age column" Click="mi_ExpandAll_Click"/>
<MenuItem Name="mi_CollapseAll" Header="None"/>
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="Gray" FontSize="16" VerticalAlignment="Bottom"/>
<TextBlock Text="{Binding ItemCount}" FontSize="22" Foreground="Green" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" FontSize="22" Foreground="Silver" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter>
<ItemsPresenter.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Item" Click="MenuItem_Click"/>
<MenuItem Header="Menu Item 2">
<MenuItem Header="Remove Item" Click="MenuItem_Click_1"></MenuItem>
<MenuItem Header="Select last Item" Click="MenuItem_Click_2"></MenuItem>
</MenuItem>
</ContextMenu>
</ItemsPresenter.ContextMenu>
</ItemsPresenter>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ListView Margin="10" Name="lv" Grid.Row="0" ItemsSource="{Binding GroupView}" SelectedIndex="{Binding Index}" SelectedItem="{Binding Item}" SelectionChanged="lv_SelectionChanged">
<ListView.View>
<GridView>
<local:GridViewColumnExt Header="Name" Width="120" DisplayMemberBinding="{Binding Name}"/>
<local:GridViewColumnExt x:Name="colAge" Header="Age" Width="50">
<local:GridViewColumnExt.CellTemplate>
<DataTemplate>
<Button Content="{Binding Age}"></Button>
</DataTemplate>
</local:GridViewColumnExt.CellTemplate>
</local:GridViewColumnExt>
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupHeaderStyle}">
</GroupStyle>
</ListView.GroupStyle>
</ListView>
I set SelectedIndex in event
private void MenuItem_Click_2(object sender, RoutedEventArgs e)
{
lv.SelectedIndex = 8;
}
Edit
I try to set IsExpanded="True", then it is work. So, if IsExpanded="False" at first, can't it scrolling?
I user expander.BringIntoView(), then it's work!

Validation in DataGrid not showing Tooltip

In my xaml I am trying to display errors in Tooltip in a datagrid. The Texblock gets its border red and the grid row shows red "!" to say there is an error but tooptip is not displayed (on hovering the mouse)
xaml is
<Window.Resources>
<!--Error Template to change the default behaviour-->
<ControlTemplate x:Key="ErrorTemplate">
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
<!--To display tooltip with the error-->
<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>
In Code my class implements IDataErrorInfo and the 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();
}
}
Also is there a way that until all validations are satisfied, the ObservableCollection is not updated?
Try this Style :
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>

How to verify a string changed from a textbox in listview is not the same as any exiting ones

I have a ListView box containing TextBoxes that allow users to add and change the content. How do I verify that the content that is changed is not the same as any exiting one in C# behind?
Xaml:
<ListView
x:Name="_regionQueryListBox"
Width="122"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
DataContext="{Binding}"
IsSynchronizedWithCurrentItem="True"
Style="{StaticResource ListViewRegionSelectorStyle}"
ItemsSource="{Binding Path=Model}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectionChanged="_regionQueryListBox_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Region" Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
MaxLength="16"
Width="110"
Margin="-2,0,0,0"
Padding="-2,0,0,0"
Text="{Binding Path=RegionName}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Yes, it is MVVM. I have a validation for adding same item and you can find the Model like below:
private void OnQueryCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if (Model.Count == 0)
{
CurrentRegionViewModel = null;
}
if (args.Action == NotifyCollectionChangedAction.Add)
{
RegionQuery addedRegionQuery = args.NewItems.OfType<RegionQuery>().FirstOrDefault();
if (addedRegionQuery != null)
{
string name = addedRegionQuery.RegionName;
while (Model.Any(q => q.RegionName == name && q != addedRegionQuery))
{
name += "*";
}
addedRegionQuery.RegionName = name;
}
}

WPF ListView: Aligning text in selected columns

<ListView ItemsSource="{Binding MyData}">
<ListView.View>
<GridView>
<GridViewColumn Header="col1" DisplayMemberBinding="{Binding Path=value1}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextAlignment="Right" Text="{Binding Path=value1}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="col2">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextAlignment="Center" Text="{Binding Path=value2}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="col3" DisplayMemberBinding="{Binding Path=value3}"/>
</GridView>
</ListView.View>
<ListView ItemsSource="{Binding MyData}">
col1 is supposed to be right-aligned. (Not working)
col2 is supposed to be center-aligned. (Working)
col3 is supposed to be left-aligned. (Working)
Is there a reason DisplayMemberBinding is overriding CellTemplate? If so, is there a fix for this (while still using DisplayMemberBinding)?
Edit: I ended up implementing it like this:
<Window xmlns:util="clr-namespace:TestProject.Util">
<Window.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
<Style TargetType="GridViewColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Left"/>
</Style>
<DataTemplate x:Key="value1Template">
<TextBlock TextAlignment="Right" Text="{Binding Path=value1}"/>
</DataTemplate>
<DataTemplate x:Key="value2Template">
<TextBlock TextAlignment="Right" Text="{Binding Path=value2}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ListView ItemsSource="{Binding MyData}" IsSynchronizedWithCurrentItem="True" util:GridViewSort.Command="{Binding SortCommand}">
<ListView.View>
<GridView>
<GridViewColumn Header="col1" CellTemplate="{StaticResource value1Template}" util:GridViewSort.PropertyName="value1"/>
<GridViewColumn Header="col2" CellTemplate="{StaticResource value2Template}" util:GridViewSort.PropertyName="value2"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
In the code behind:
private RelayCommand sortCommand;
public ICommand SortCommand { get { return sortCommand ?? (sortCommand = new RelayCommand(Sort)); } }
private void Sort(object param)
{
var propertyName = param as string;
var view = CollectionViewSource.GetDefaultView(MyData);
var direction = ListSortDirection.Ascending;
if (view.SortDescriptions.Count > 0)
{
var currentSort = view.SortDescriptions[0];
if (currentSort.PropertyName == propertyName)
direction = currentSort.Direction == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
view.SortDescriptions.Clear();
}
if (!string.IsNullOrEmpty(propertyName))
view.SortDescriptions.Add(new SortDescription(propertyName, direction));
}
DisplayMemberBinding has the highest priority. You can not use it combined with CellTemplate. See here in the remarks section.
If you want to right-or center-align the content, you must declare the CellTemplate with the binding (as you did) and remove the DisplayMemberBinding-attribute. If you also want to change the column header alignment, you must also set the GridViewColumn.Header-property.
Just add the following after your Window tag:
<Window.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Right" />
</Style>
</Window.Resources>

Categories