In a WPF Project I have a set of record objects with the properties School, Subject, FirstName and LastName. The records are grouped by School and Subject using CollectionViewSource in XAML and this is used by a TreeView to show the grouped items. The groupings work fine.
The problem is I would like to show the FirstName and LastName of the records under the Subject in a ListView using a GridView as its View with FirstName and LastName as columns, but I can't figure out how to do this.
Here is an image of the current display:
Here's some sample code to show what I mean.
public class Record
{
public string School { get; set; }
public string Subject { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var records = new Record[]
{
new Record() { School = "School A", Subject = "Maths" , FirstName = "Fred" , LastName = "Blogs"},
new Record() { School = "School A", Subject = "English" , FirstName = "Alice" , LastName = "Lane"},
new Record() { School = "School B", Subject = "Geography" , FirstName = "John" , LastName = "Smith"},
new Record() { School = "School B", Subject = "Geography" , FirstName = "Burt" , LastName = "Lancaster"},
new Record() { School = "School C", Subject = "Chemistry" , FirstName = "Dee" , LastName = "Kaye"}
};
this.DataContext = records;
}
}
XAML :
<Window x:Class="ListViewInTreeView.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:ListViewInTreeView"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource x:Key="listings"
Source="{Binding .}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="School" />
<PropertyGroupDescription PropertyName="Subject" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid>
<TreeView Grid.Row="4" DataContext="{StaticResource listings}" ItemsSource="{Binding}" >
<TreeView.GroupStyle>
<GroupStyle>
<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 Margin="0,0,0,0" IsExpanded="True" BorderBrush="#FFA4B97F"
BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/>
<TextBlock Text=" : "/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
<TextBlock Text=" items(s)"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter Margin="20,0,0,0" />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</TreeView.GroupStyle>
<TreeView.Resources>
<!-- NEED HELP HERE I THINK ?-->
<HierarchicalDataTemplate DataType="{x:Type GroupItem}">
<ListView ItemsSource="{Binding .}">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}"/>
<GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
</GridView>
</ListView.View>
</ListView>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
</Window>
Use 2 GroupStyles, one for the School group and another one for the Subject group:
<TreeView Grid.Row="4" DataContext="{StaticResource listings}" ItemsSource="{Binding}" >
<TreeView.GroupStyle>
<GroupStyle>
<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 Margin="0,0,0,0" IsExpanded="True" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/>
<TextBlock Text=" : "/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
<TextBlock Text=" items(s)"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter Margin="20,0,0,0" />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<GroupStyle>
<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 Margin="0,0,0,0" IsExpanded="True" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/>
<TextBlock Text=" : "/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
<TextBlock Text=" items(s)"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}"/>
<GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
</GridView>
</ListView.View>
</ListView>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</TreeView.GroupStyle>
</TreeView>
You probably also want to move the common property settings for the Expander and GroupItem to two styles that are used by both GroupStyles:
<TreeView Grid.Row="4" DataContext="{StaticResource listings}" ItemsSource="{Binding}" >
<TreeView.Resources>
<Style TargetType="GroupItem">
<Setter Property="Margin" Value="0,0,0,5"/>
</Style>
<Style TargetType="Expander">
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="IsExpanded" Value="True" />
<Setter Property="BorderBrush" Value="#FFA4B97F" />
<Setter Property="BorderThickness" Value="0,0,0,1" />
<Setter Property="Header" Value="{Binding}" />
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/>
<TextBlock Text=" : "/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
<TextBlock Text=" items(s)"/>
</DockPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
<TreeView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Content>
<ItemsPresenter Margin="20,0,0,0" />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Content>
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}"/>
<GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
</GridView>
</ListView.View>
</ListView>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</TreeView.GroupStyle>
</TreeView>
Related
I have a WPF application where I have view which lets User choose list of fields at run-time and I am storing in a Text file and I am trying to create a Data entry form based on the list of fields that user created Run-time.
I have developed an solution using code behind but I am trying to implement this using MVVM.
Approach 1: I can create the textBlock and Textbox in the code-behind and bind it to the properties in the Viewmodel. The viewmodel will have all the possible field property.
<TabControl Margin="335,10,10,71" TabStripPlacement="Bottom">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Name="Border"
Margin="0,0,0,0"
Background="Transparent"
BorderBrush="Black"
BorderThickness="1,1,1,1"
CornerRadius="5">
<ContentPresenter
x:Name="ContentSite"
Margin="12,2,12,2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ContentSource="Header"
RecognizesAccessKey="True">
<ContentPresenter.LayoutTransform>
<RotateTransform Angle="0" />
</ContentPresenter.LayoutTransform>
</ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Height" Value="40" />
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Panel.ZIndex" Value="200" />
<Setter TargetName="Border" Property="Background" Value="Black" />
<Setter TargetName="Border" Property="BorderBrush" Value="White" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
<Setter Property="Foreground" Value="White" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="Bold" />
<Setter TargetName="Border" Property="Background" Value="Black" />
<Setter TargetName="Border" Property="BorderBrush" Value="White" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Header="Product Data 1">
<Grid x:Name="Grid1">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="Column_1" Width="*" />
<ColumnDefinition x:Name="Column_2" Width="*" />
</Grid.ColumnDefinitions>
<Border
Margin="0,0,0,10"
Background="LightSkyBlue"
BorderBrush="Gray"
BorderThickness="2,2,2,2">
<StackPanel
x:Name="MainStack"
Margin="20,0,0,0"
x:FieldModifier="public"
Grid.IsSharedSizeScope="True"
Orientation="Vertical" />
</Border>
<Border
Grid.Column="1"
Margin="0,0,0,10"
Background="LightSkyBlue"
BorderBrush="Gray"
BorderThickness="2,2,2,2">
<StackPanel
x:Name="SecondStack"
Grid.Column="1"
Margin="20,0,0,0"
x:FieldModifier="public"
Grid.IsSharedSizeScope="True"
Orientation="Vertical" />
</Border>
<!--<StackPanel x:Name="ThirdStack" x:FieldModifier="public" Grid.Column="2" Orientation="Vertical" Grid.IsSharedSizeScope="True" Width="auto" Height="476" VerticalAlignment="Center" HorizontalAlignment="Center" />
<Border BorderBrush="Black" BorderThickness="1,1,1,1" Grid.Column="2" Margin="0,0,0,10"/>-->
</Grid>
</TabItem>
</TabControl>
Code-Behind=
string[] Data = File.ReadAllLines("Field.txt");
foreach (var part in Data)
{
Grid mytxtBStack = new Grid();
MainStack.Children.Add(mytxtBStack);
ColumnDefinition column_1 = new ColumnDefinition();
column_1.SharedSizeGroup = "FirstColumn";
ColumnDefinition column_2 = new ColumnDefinition();
column_2.SharedSizeGroup = "SecondColumn";
mytxtBStack.ColumnDefinitions.Add(column_1);
mytxtBStack.ColumnDefinitions.Add(column_2);
txtBlock = new TextBlock();
txtBlock.Name = "MyBlock" + i;
txtBlock.Text = part;
txtBlock.FontSize = 14;
txtBlock.FontWeight = FontWeights.DemiBold;
txtBlock.Foreground = Brushes.Black;
txtBlock.Margin = new Thickness(0, 20, 0, 0);
mytxtBStack.Children.Add(txtBlock);
txtBox = new TextBox();
txtBox.Name = "MyText" + i.ToString();
txtBox.FontSize = 12;
txtBox.Height = 25;
txtBox.Width = 250;
txtBox.BorderThickness = new Thickness(1, 1, 1, 1);
txtBox.Margin = new Thickness(130, 20, 0, 0);
txtBox.Foreground = Brushes.Black;
txtBox.BorderBrush = Brushes.Black;
txtBox.Background = Brushes.White;
txtList.Add(txtBox);
mytxtBStack.Children.Add(txtBox);
}
Approach 2: I can create a dynamic class using the Expandoobject based on the Textfile and create a itemscontrols to bind with view and mentioned all the possibile field properties in the Viewmodel.
I am thinking about implementing this part with
inside the TabControl,
<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Fields">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<TextBox Width="300" Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Can anyone please suggest me a way to do this with MVVM wpf?
I implemented this using the CommunityToolkit.Mvvm NuGet package.
MainWindowViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
namespace WpfApp1;
public class Field
{
public string Block { get; set; } = string.Empty;
public string Text { get; set; } = string.Empty;
}
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
// CommunityToolkit's source generator will create a "Field" property.
private ObservableCollection<Field> fields = new();
[RelayCommand]
// CommunityToolkit's source generator will create a "LoadFieldsCommand" command.
private void LoadFields()
{
for (int i = 0; i < 10; i++)
{
Fields.Add(new Field() { Block = $"Block#{i + 1}", Text = $"Text{i + 1}" });
}
}
[ObservableProperty]
// Bind this to the Text property.
private string textBlockText = string.Empty;
[ObservableProperty]
// Bind this to the ItemsSource property.
private List<string> comboBoxItems = new()
{
"Item A",
"Item B",
"Item C",
};
}
MainWindow.xaml.cs
using System.Windows;
namespace WpfApp1;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MainWindowViewModel ViewModel { get; } = new();
}
MainWindow.xaml
<Window
x:Class="WpfApp1.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:local="clr-namespace:WpfApp1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="ThisWindow"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button
Grid.Row="0"
Command="{Binding ElementName=ThisWindow, Path=ViewModel.LoadFieldsCommand}"
Content="Load fields" />
<ItemsControl Grid.Row="1" ItemsSource="{Binding ElementName=ThisWindow, Path=ViewModel.Fields}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:Field">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Block}" />
<TextBox Text="{Binding Text}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
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>
I want make a footer to my DataGrid using this method:
How do I add a footer row in a WPF datagrid?
But, i need to make this by code behind, and that's don't working.
I think it's because the binding of the grid can't find the column.
private DataGridColumn dgInsertCol(ref int idx, DataGridColumn dgc, tblEtatRecapColonne pCol, string pBinding = "") {
var dhHeadName = $"dgHead{pCol.Id}";
dgc.SetValue(NameProperty, dhHeadName);
dgc.HeaderTemplate = GetDtHeader(pCol, pBinding);
dgc.Width = new DataGridLength(1.0, (ckbSize.IsChecked.Value) ? DataGridLengthUnitType.Star : DataGridLengthUnitType.SizeToCells);
dgMain.Columns.Insert(idx++, dgc);
// Faire le footer associé
var t = new TextBlock() { Margin = new Thickness(5, 0, 0, 0), Text = pBinding, Background = new SolidColorBrush(Color.FromRgb(50, 100, 150)) };
var g = new Grid() { MinWidth = 10 };
g.SetBinding(Grid.WidthProperty, new Binding("ActualWidth") { ElementName = dhHeadName, Path=new PropertyPath("ActualWidth", null) }); // DataGridColumn
g.Children.Add(t);
pnlDgFooter.Children.Add(g);
return dgc;
}
<DataGrid Grid.Row="1" 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>
<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="True" BorderThickness="1,1,1,5">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<!-- Style for groups under the top level. -->
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<DockPanel Background="LightBlue">
<TextBlock Text="{Binding Path=Name}" Foreground="Blue" Margin="30,0,0,0" Width="100"/>
<TextBlock Text="{Binding Path=ItemCount}" Foreground="Blue"/>
</DockPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<StaticResource ResourceKey="rowBtnDetail"/>
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="2" Name="pnlDgFooter" HorizontalAlignment="Left" Orientation="Horizontal" >
</StackPanel>
Why use ElementName at all? Set the Source of the Binding directly. Also, don't set Path twice, so either write
g.SetBinding(Grid.WidthProperty,
new Binding
{
Source = dgc,
Path = new PropertyPath("ActualWidth")
});
or
g.SetBinding(Grid.WidthProperty, new Binding("ActualWidth") { Source = dgc });
I'm talking about DataGrid not DataGridView!
What I'm doing here is refreshing datagrid which contains <DataGrid.GroupStyle> and I'm showing my data from database grouped by number of order, and what I want to do is refresh my datagrid.
Here is my code:
public MainWindow()
{
try
{
InitializeComponent();
this.WindowStartupLocation = WindowStartupLocation.CenterScreen;
this.WindowState = WindowState.Maximized;
var ordersList = OrdersController.localOrders();
collectionViewSource.Source = ordersList;
collectionViewSource.GroupDescriptions.Add(new PropertyGroupDescription("NumberOfOrder"));
DataContext = collectionViewSource;
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(8);
timer.Tick += timer_Tick;
timer.Start();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void timer_Tick(object sender, EventArgs e)
{
var ordersList = OrdersController.localOrders();
collectionViewSource.Source = null;
collectionViewSource.Source = ordersList;
DataContext = collectionViewSource;
datagrid1.ScrollIntoView(datagrid1.Items[datagrid1.Items.Count - 1]);
}
}
XAML:
<DataGrid HorizontalAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Hidden" BorderBrush="#83D744" IsSynchronizedWithCurrentItem="False" VerticalGridLinesBrush="Transparent" Grid.Column="0" RowHeaderWidth="0" CanUserAddRows="False" AutoGenerateColumns="False" x:Name="datagrid1" Margin="10,150,8,50" Background="Transparent" RowBackground="#FF494949" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" ItemsSource="{Binding}">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="#83D744"/>
<Setter Property="Opacity" Value="1"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="18"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="Height" Value="50"/>
</Style>
<Style x:Key="TextInCellCenter" TargetType="{x:Type TextBlock}" >
<Setter Property="TextAlignment" Value="Center"/>
</Style>
<Style TargetType="{x:Type TextBlock}" x:Key="RightAligElementStyle">
<Setter Property="TextAlignment" Value="Right"/>
</Style>
<Style TargetType="{x:Type TextBlock}" x:Key="LeftAligElementStyle">
<Setter Property="TextAlignment" Value="Left"/>
</Style>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="Transparent"/>
</DataGrid.Resources>
<DataGrid.Columns >
<DataGridTextColumn Binding="{Binding ProductName}" ElementStyle="{StaticResource LeftAligElementStyle}" Header="NAZIV ARTIKLA" MinWidth="350" Foreground="White" FontSize="20" FontFamily="Verdana" />
<DataGridTextColumn Binding="{Binding Quantity}" ElementStyle="{StaticResource TextInCellCenter}" Header="KOLIČINA" MinWidth="200" Foreground="White" FontSize="20" FontFamily="Verdana" />
</DataGrid.Columns>
<DataGrid.GroupStyle>
<!-- Style for groups at top level. -->
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" Background="Black" Opacity="0.7">
<Expander.Header >
<DockPanel Height="50" Margin="0,0,0,0" Name="dockPanel" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}, Path=ActualWidth}">
<Button Name="btnFinishOrder" Content="Finish Order" Margin="0,0,55,5" DockPanel.Dock="Right" Click="btnFinishOrder_Click" FontSize="12" BorderThickness="1.5" HorizontalAlignment="Left" VerticalAlignment="Bottom" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Foreground="#83D744" Background="Transparent" BorderBrush="#83D744" Width="130" Height="40">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush= "{TemplateBinding BorderBrush}"
Background= "{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
<Button Name="btnTakeIt" Click="btnTakeIt_Click" Content="Take it" Margin="0,0,20,5" DockPanel.Dock="Right" FontSize="12" BorderThickness="1.5" HorizontalAlignment="Left" VerticalAlignment="Bottom" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Foreground="#83D744" Background="Transparent" BorderBrush="#83D744" Width="130" Height="40">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
<TextBlock FontWeight="Normal" FontFamily="Verdana" FontSize="20" Height="25" Foreground="#83D744" Text="{Binding Path=Name,StringFormat= Number of Order:# {0}}" />
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
after reload data, cell selection jumps on first column and scrollbars is reset. How to save position of DataGrid?
If you need to scroll to specific item, just save the index and use:
dataGrid.ScrollIntoView(dataGrid.Items[itemIndex]);
You can use this to scroll to the last item in the DataGrid:
dataGrid.ScrollIntoView(dataGrid.Items[dataGrid.Items.Count - 1]);
EDIT:
There seems to be a problem with DataGrid scrolling when using CollectionViewSource and grouping.
I managed to get it to work the other way... Instead of
dataGrid.ScrollIntoView(dataGrid.Items[dataGrid.Items.Count - 1])
try using the following code to do the scrolling:
if (datagrid1.Items.Count > 0)
{
var border = VisualTreeHelper.GetChild(datagrid1, 0) as Decorator;
if (border != null)
{
var scrollViewer = border.Child as ScrollViewer;
if (scrollViewer != null) scrollViewer.ScrollToEnd();
}
}
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.