Datagrid expander (from grouping) header - wpf

Following this tutorial I had an idea to put in the Expander Header more data.
I have 2 tables (Document 1 - * Entry).
I'm displaying the Entries grouped by Documents and I don't want some data to be repeated in the
datagrid so I thought to place it in the expander header.
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text=" - "/>
**<TextBlock Text="{Binding Path=Document.Number or Name2}"/>**
</StackPanel>
...

You can do this:
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GroupItem}}, Converter={StaticResource ResourceKey=groupToTitleConverter}}" />
</StackPanel> </Expander.Header>
Converter:
public class GroupToTitleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
GroupItem groupItem = value as GroupItem;
CollectionViewGroup collectionViewGroup = groupItem.Content as CollectionViewGroup;
EntryViewModel entryViewModel = collectionViewGroup.Items[0] as EntryViewModel;
string title = string.Format("{0} - {1} {2}", entryViewModel.Id, entryViewModel.Numar, entryViewModel.Obiect);
return title;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Taking first item from collection in group to form header title might not be the most elegant solution but it will serve the purpose.
Full code is available here: ExpanderHeadersInDataGridGroupStyle.zip

Related

How to check the property value of the singleton(or static) class in the XAML?

There may be expressions that sound rude because I'm not a native English speaker.
I hope you to understand.
I am creating an application supporting the theme.
I would like to change the image in the XAML whenever the theme changes.
Currently, my requirements were implemented by using both cs code and XAML code as below.
<HierarchicalDataTemplate DataType="{x:Type models:SolutionStruct}" ItemsSource="{Binding Projects}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Converter={converters:ToImageByThemeConverter}, ConverterParameter='Solution'}" Width="16" Height="16" Margin="0 0 5 0">
</Image>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
The below code is the cs code to convert.
public class ToImageByThemeConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string result = string.Empty;
if (Theme.ThemeKind == ThemeKind.Dark)
{
if (parameter.ToString() == "Solution") result = "/Resources/solution.png";
else if (parameter.ToString() == "Project") result = "/Resources/project.png";
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}
The above code works well but I would like to implement the equal functionality by using only XAML code.
I think If I can check the property value of the Theme class then I can solve it.
Here are some of the vague codes that I hit upon to solve this problem. (not operate)
<HierarchicalDataTemplate DataType="{x:Type models:SolutionStruct}" ItemsSource="{Binding Projects}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="0 0 5 0">
<Image.Triggers>
<Trigger Binding Source="{Binding {x:Static Theme}, Path="{ThemeKind}"} Value="Dark">
<Setter Source="/Resources/solution_dark.png"/>
</Trigger>
</Triggers>
</Image>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
Of course, the above code does not operate because I design vaguely.
Is it possible to design a code that operates with a similar feel to the above code?
or if you have another way to solve this problem please let me know, I don't obsess my way.
Thank you for reading.
You can bind to static properties by enclosing the property path in parentheses.
Adding the corresponding DataTrigger to the DataTemplate the HierarchicalDataTemplate would become:
<HierarchicalDataTemplate DataType="{x:Type models:SolutionStruct}"
ItemsSource="{Binding Projects}">
<StackPanel Orientation="Horizontal">
<Image x:Name="Image"
Source="/Resources/solution.png" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=(Theme.ThemeKind)}" Value="{x:Static ThemeKind.Dark}">
<Setter TargetName="Image" Property="Source" Value="/Resources/solution_dark.png" />
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>

WPF Radcalender customization day template format

I want to cretae wpf radcalender in my wpf mvvm application. I can't able to customize the calender day template. I want to this format.
I am using this code. But the text has been overwrite the date.
xmal:
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test"/>
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
I've worked with the RadCalendar within a Winrt app and here how i fixed that (with some help from the official Docs)
First add a new converter that will return the Date Label like that:
public class CellModelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var cellModel = value as CalendarCellModel;
return cellModel.Label;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
then in you Xaml add a textBlock to display the Cell Date using the previous converter :
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test"/>
<TextBlock Text="{Binding Converter={StaticResource CellModelConverter}}" FontSize="13.333" VerticalAlignment="Bottom" Margin="6,0,0,4" />
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
and don't forget to add the Converter to the Static Resources:
<converters:CellModelConverter x:Key="CellModelConverter" />
--EDIT 1
the code above work fine in a Winrt application, for a WPF project you don't need a converter at all :
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test"/>
<TextBlock Text="{Binding}" FontSize="13.333" VerticalAlignment="Bottom" Margin="6,0,0,4" />
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
--EDIT 2
the above code shows the "test" text in all the cells including WeekName, DayName ..., to fix that you can use either a Converter or a DataTrigger, here how to do it using a DataTrigger :
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Visibility" Value="Collapsed"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.ButtonType,ElementName=tb}"
Value="Date">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBlock x:Name="tb" Text="{Binding Converter={StaticResource CellModelConverter}}" FontSize="13.333" VerticalAlignment="Bottom" Margin="6,0,0,4" />
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
Output:

Text separator between ListView in WPF

I'm trying to get the following from a ListView:
Text | Text | Text
I've already achieved the vertical orientation by the following
<ItemsPanelTemplate><StackPanel Orientation="Horizontal"/></ItemsPanelTemplate>
Each part(Text) is a TextBlock bound to a string in MVVM.
Preferably the lines between should just be regular vertical bars.
Any tips for achieving these vertical bars as specified??
Duplicate of How can a separator be added between items in an ItemsControl, try this:
<ItemsControl Name="theListBox">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="seperator" Text=" | "/>
<TextBlock Text="{Binding}"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}" Value="{x:Null}">
<Setter Property="Visibility" TargetName="seperator" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In case you have achieved to show separator in between two items, you can have a converter say LastItemInContainerToVisibilityConverter which will be binded to the visibility of separator making separator collapsed for last item and visible for all other items.
Assuming you have used Rectangle to show the seperation between items -
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}"/>
<Rectangle Visibility="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=ListViewItem},
Converter={StaticResource
LastItemInContainerToVisibilityConverter}}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
Here goes your converter which will return if it's last item in collection -
public class LastItemInContainerToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
DependencyObject item = (DependencyObject)value;
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);
return (ic.ItemContainerGenerator.IndexFromContainer(item)
== ic.Items.Count - 1) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}

Show tick image when item is selected in WPF Listbox

I have this simple ListBox which displays a list of images horizontal en vertical.
I have also added a tick image on every image. Now I would like to enable this tick image only when the item is selected in the Listbox.
How can I achieve this?
ListBox XAML:
<ListBox x:Name="PhotoCollection"
Grid.Row="2"
Grid.ColumnSpan="4"
ItemsSource="{Binding PhotoCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectionMode="Multiple">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Border BorderBrush="White"
BorderThickness="2"
Margin="5"
Background="LightGray">
<Grid>
<Image Source="{Binding}"
Stretch="Uniform"
Width="50"
Height="50"
Margin="5" />
<Image Source="{StaticResource Check_24}"
Visibility="{Binding Converter={StaticResource VisibleConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}, AncestorLevel=1},Path=IsSelected}"
Stretch="Uniform"
Width="20"
Height="20"
Margin="5"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"/>
</Grid>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
EDIT: This line does the trick
Visibility="{Binding Converter={StaticResource VisibleConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}, AncestorLevel=1},Path=IsSelected}"
It should work when you bind the IsSelected property of the ListBoxItem to your property IsVisible. But it depends on how you have implemented your ViewModel and the properties.
<ListBox>
<!-- the rest of the XAML-Definition of your ListBox -->
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsVisible, Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
When I need to bind visibility of items to boolean values, I've been using a converter class:
public class BoolToVisibleOrHidden : IValueConverter {
public BoolToVisibleOrHidden() { }
public bool Collapse { get; set; }
public bool Reverse { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
bool bValue = (bool)value;
if (bValue != Reverse) {
return Visibility.Visible;
} else {
if (Collapse)
return Visibility.Collapsed;
else
return Visibility.Hidden;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
Visibility visibility = (Visibility)value;
if (visibility == Visibility.Visible)
return !Reverse;
else
return Reverse;
}
}
After grabbing an instance of it in XAML like so :
<local:BoolToVisibleOrHidden x:Key="BoolToVisConverter" Collapse="True" />
I can bind visibility of items like this:
Visibility="{Binding Converter={StaticResource BoolToVisConverter}, Path=DataContext.PATHTOBOOLEAN}"

WPF binding TextBox and ObservableDictionary<Int64,String> (display String by Id)

Each item of my Employee's list has Post property. This property is Int64 type. Also, I have some ObservableDictionary<Int64,String> as static property. Each Employe must display the String value by its key.
DataTemplate for Employe item (I deleted the superfluous):
<DataTemplate x:Key="tmpEmploye">
<Border BorderThickness="3" BorderBrush="Gray" CornerRadius="5">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=Post}"/>
</StackPanel>
</Border>
</DataTemplate>
But this code displayed the Int64 value, not the String. String for getting static dictionary:
"{Binding Source={x:Static app:Program.Data}, Path=Posts}"
I know how solve it problem for ComboBox, but I don't know for TextBlock. For ComboBox I wrote it (it is works fine):
<ComboBox x:Name="cboPost" x:FieldModifier="public" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="2" Grid.ColumnSpan="2"
ItemsSource="{Binding Source={x:Static app:Program.Data}, Path=Posts}" DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding Path=Post, Mode=TwoWay}">
</ComboBox>
But how can I solve it for TextBlock?
mmmmm, I'm sure I have developed something for this scenario before but I can't remember or find anything related!
IMO you can use a converter, so you pass your Post (Int64) to the converter and it returns the string value from the dictionary, although it must be a better solution.
[ValueConversion(typeof(Int64), typeof(string))]
public class PostToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// validation code, etc
return (from p in YourStaticDictionary where p.Key == Convert.ToInt64(value) select p.Value).FirstOrDefault();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
}
}
XAML:
<Window ...
xmlns:l="clr-namespace:YourConverterNamespace"
...>
<Window.Resources>
<l:PostToStringConverter x:Key="converter" />
</Window.Resources>
<Grid>
<TextBlock Text="{Binding Post, Converter={StaticResource converter}}" />
</Grid>
</Window>

Resources