WPF Grouped ListBox bound to CollectionView - wpf

I am trying to find a way to code a list of grouped users and be able to dynamically display their work that is assigned to them. I have an Observable Collection (UsersOC) that is bound to a CollectionViewSource in my XAML (Defined in the Resources section of the User Control) like so:
<CollectionViewSource x:Key="UserQueueList" Source="{Binding QueueList}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Name"></PropertyGroupDescription>
</CollectionViewSource.GroupDescriptions>
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Name"/>
<scm:SortDescription PropertyName="EmployeeNumber"/>
<scm:SortDescription PropertyName="SortOrder"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
So what I have is a list of all the users and their related work.
I have to represent every driver, with or without work, in a list (grouped by user name) and then their work. If they have no work I need to show a that says "Drop work here" which is a Drop zone which will add the work to their queue. I have to have the control be a ListBox under the driver name, so it will look like this:
--> Driver Name1
- work 1
- work 2
--> Driver Name2
Drop Work Here
--> Driver Name3
- work 1
- work 2
- work 3
the areas with the work items listed, again, are/should be list boxes and can be individually dragged and dropped (i have that portion of the code already). Here is what I have so far:
<ItemsControl ItemsSource="{Binding Source={StaticResource TrailerCardQueueList}}" Grid.ColumnSpan="2" Grid.Row="1" BorderBrush="Transparent"
Background="Transparent" Name="lvDriverQueueWork" Margin="10,0,10,0" Foreground="Black">
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock FontSize="14" FontWeight="SemiBold">
<Run Text="{Binding Path=Items[0].EmployeeNumber}"/>
<Run Text="-"/>
<Run Text="{Binding Name, Mode=OneWay}"/>
</TextBlock>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
//I figured I could hide/show the area here with the default drop zone if they have no work, but this doesn't work.
<DataTrigger Binding="{Binding Path=HasItems, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Value="False">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Grid.Row="0" Grid.Column="0" Text="DROP TRAILER CARD HERE"/>
</Grid>
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding HasItems, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Grid.Column="1" Text="{Binding , UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Grid.Column="2" Text="{Binding , UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Related

Change displayed text in XAML Combobox multi selection

I have the following code to allow users to select multiple items from a combobox. However when they click one item, it makes this the displayed text when combobox closes. Can I change the displayed text to something that isnt just the item selected. For example if the users select items A,B and D, I want the text part of the combobox to show "A, B, D"
<ComboBox ItemsSource="{Binding ListOfItems}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Width="20" />
<TextBlock Text="{Binding DisplayName}" Width="110" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Thanks
You could use a ContentControl with a Style that changes the ContentTemplate property for the selected item. The following sample markup should give you the idea.
<ComboBox ItemsSource="{Binding ListOfItems}">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate">
<Setter.Value>
<!-- the template for the items in the dropdown list -->
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Width="20" />
<TextBlock Text="{Binding DisplayName}" Width="110" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ComboBoxItem}}" Value="{x:Null}">
<Setter Property="ContentTemplate">
<Setter.Value>
<!-- the template for the selected item-->
<DataTemplate>
<ItemsControl ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=ComboBox}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" Margin="0 0 5 0"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Please refer to the following similar question for more information.
Can I use a different Template for the selected item in a WPF ComboBox than for the items in the dropdown part?

How to add CheckBox content from resource file in wpf?

I have a combobox in which first item is checkbox to select all entries from below drop down list. With hard coded string its working fine but now I want to localize it...want to set string from resource file but as its deeply inserted couldn't add it. For that I tried adding panel and one checkbox along with textblock also simply separated textblock from checkbox.
This is code :
<ComboBox Name="CmbEntries" IsTabStop="{Binding CurrentDialogViewModel, Converter={StaticResource NullToBoolConverter}}" Grid.Column="1" Grid.Row="4" VerticalAlignment="Top" Margin="2,0,0,5" SelectedItem="{Binding SelectedEntries}" SelectedIndex="0" HorizontalAlignment="Left" Width="400">
<ComboBox.Resources>
<CollectionViewSource x:Key="EntriesCollection" Source="{Binding DicomDir.Entries}"/>
</ComboBox.Resources>
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem>
<CheckBox x:Name="all" Margin="8,0,0,0"
IsChecked="{Binding IsAllEntriessSelected, Mode=TwoWay}"
Command="{Binding SelectAllEntriessCommand}"
CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}"
**Content="Select All Entries"** />
</ComboBoxItem>
<CollectionContainer Collection="{Binding Source={StaticResource EntriesCollection}}"/>
</CompositeCollection>
</ComboBox.ItemsSource>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Margin="8,0,0,0"
IsChecked="{ Binding Path=IsAnyEntitySelected, Mode=TwoWay }"
Command="{Binding SelectAllEntityCommand}"
CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}" />
<TextBlock Margin="5,0,0,0" TextWrapping="Wrap" Text="{Binding DisplayEntriesInfo}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding HasSelectedEntities}" Value="True">
<Setter Property="FontWeight" Value="Bold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
Scrshot1-Expected
Scrshot2-Actual with Resx
In this case, is it possible to read it directly from resource file?
To bind the content to a resource you do:
<Window ...
xmlns:properties="clr-namespace:YourNamespace.Properties">
<Grid>
<CheckBox Content="{x:Static properties:Resources.Foo}" />
</Grid>
</Window>
For this to work you need Resources to be public.
If it is not you get:
Provide value on 'System.Windows.Markup.StaticExtension' threw an exception...
StaticExtension value cannot be resolved to an enumeration, static field, or static property.

Wpf Treeview Contextmenu DataContext

I am trying to set the Datacontext of my Contextmenu - but my Code does not work. Very similar code works at another location, so I would be grateful if somebody could explain why it does not work.
My Treeview looks like this:
Beware: it is rather long, but I think the nested structure is part of the problem, so I do want to shrink it.
The Problem occurs on the second "level" at the ppChart Binding.
<TreeView x:Name ="Presentation_SlidesWithIndex" Grid.ColumnSpan="1" HorizontalAlignment="stretch" Height="auto" Margin="0,0,3,0" VerticalAlignment="stretch" Width="auto" Tag="{Binding DataContext, ElementName=LayoutRoot}">
<TreeView.ItemContainerStyle>
<!--expand Charts when they contain elements-->
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding HasCharts}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type pp:PPSlide}" ItemsSource="{Binding Charts}">
<StackPanel x:Name="PPSlideElements" Orientation="Horizontal">
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Style.Triggers>
<!--Trigger for Slides with Charts-->
<DataTrigger Binding="{Binding Path=HasCharts}" Value="True" >
<Setter Property="Background" Value="LightBlue" />
</DataTrigger>
<!--Trigger for Slides with NO Charts-->
<DataTrigger Binding="{Binding Path=HasCharts}" Value="False" >
<Setter Property="Opacity" Value=".5" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock FontSize="15">
<Run Text="Slide "></Run>
<Run Text="{Binding Path=Index}"></Run>
</TextBlock>
<!--<Image Source="/Images/pptIcon.png" Height="10"></Image>-->
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type pp:PPChart}" ItemsSource="{Binding ExcelSource}">
<StackPanel x:Name="PpChartElements" Orientation="Horizontal" AllowDrop="True" cal:Message.Attach="[Event Drop] = [Action DropItem($eventArgs,$view)]" Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}">
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Style.Triggers>
<!--Trigger for Charts with DataSource set-->
<DataTrigger Binding="{Binding Path=HasDataSourceSet}" Value="True" >
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
<!--Trigger for Charts with DataSource nit set-->
<DataTrigger Binding="{Binding Path=HasDataSourceSet}" Value="False" >
<Setter Property="Background" Value="Orange" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Image Source="{Binding ChartType, Converter={StaticResource PowerPointChartTypeConverter}}" Width="19" Height="19" Margin="2,2,4,2"></Image>
<TextBlock FontSize="14" Text="{Binding Path=ShapeName}" VerticalAlignment="Center">
<TextBlock.ContextMenu>
<ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag.AddEntityCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}">
<MenuItem Header="Löse Verknüpfung" cal:Message.Attach="DeleteLink($datacontext)" ToolTip="Löscht einen bestehenden Link zu einer Excel Datei"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
<Image Source="{Binding PowerPointWriteStatus, Converter={StaticResource PowerPointWriteStatusConverter}}" Width="19" Height="19" Margin="2,2,4,2" ToolTip="Zeigt erfolg oder misserfolg des Erstellens an"></Image>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type pp:PPSourceExcelLink}">
<StackPanel x:Name="PpSourceExcelLinkStackPanel" Orientation="Vertical">
<TextBlock FontSize="14">
<Run Text="Datei: "></Run>
<Run Text="{Binding Path=ExcelFileName, Mode=OneWay}"></Run>
</TextBlock>
<TextBlock FontSize="14">
<Run Text="Tabelle: "></Run>
<Run Text="{Binding Path=SourceTableID}"></Run>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I think your problem lies exactly in this line of code:
cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag.AddEntityCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
To fix it you need to change this to:
cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag.AddEntityCommand, RelativeSource={RelativeSource Self}}"
but this alone won't fix your problem because the target of the action message will be the TextBlock which doesn't have its Tag property set.
Anyway you should end up with code similar to this:
<TextBlock FontSize="14" Text="{Binding Path=ShapeName}" VerticalAlignment="Center" Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}">
<TextBlock.ContextMenu>
<ContextMenu cal:Action.TargetWithoutContext="{Binding RelativeSource={RelativeSource Self},Path=PlacementTarget.Tag}">
<MenuItem Header="Löse Verknüpfung" cal:Message.Attach="DeleteLink($datacontext)" ToolTip="Löscht einen bestehenden Link zu einer Excel Datei" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
I answered a similar question recently and you can take a look at for more details.
I found a Solution which works fairly well - but why it works is beyond me:
<TextBlock FontSize="14" Text="{Binding Path=ShapeName}" VerticalAlignment="Center" Tag="{Binding DataContext, RelativeSource={AncestorType=TreeView}}">
<TextBlock.ContextMenu>
<ContextMenu cal:Action.TargetWithoutContext="{Binding RelativeSource={RelativeSource Self},Path=PlacementTarget.Tag}">
<MenuItem Header="Löse Verknüpfung" cal:Message.Attach="DeleteLink($datacontext)" ToolTip="Löscht einen bestehenden Link zu einer Excel Datei" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
Note how the Ancestertype treeview is used. my bet guess is that it walks the Visual tree until it finds the Treeview, which has the proper DataContect.
Thanks to Sniffer, which did a very good job !
As stated in RelativeSource binding from a ToolTip or ContextMenu there is no need to add the tag to the PlacementTarget. This makes it shorter, as there is no need to add a tag to the control and to the binding path of the ContextMenu, e.g.:
<TextBlock FontSize="14" Text="{Binding Path=ShapeName}" VerticalAlignment="Center">
<TextBlock.ContextMenu>
<ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Löse Verknüpfung" cal:Message.Attach="DeleteLink($datacontext)" ToolTip="Löscht einen bestehenden Link zu einer Excel Datei" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
and in the ViewModel
public void DeleteLink(object parameter)
{
if (parameter == null)
return;
var param = parameter as YourDataContextType;
if (! (param is YourDataContextType))
return;
// your delete handling
}

Conditional formatting for the Header content

I have the following template for some header:
<telerik:RadExpander.Header>
<StackPanel Orientation="Horizontal" DataContext="{Binding CurrentItem, ElementName=activityProductGrid}">
<TextBlock Text="{Binding Strings[Product], Source={StaticResource LanguageResources}, StringFormat='{}{0}: '}" />
<TextBlock Text="{Binding Product.Name}" FontWeight="Bold" />
<TextBlock Text="{Binding Strings[GroupName], Source={StaticResource LanguageResources}, StringFormat=', {0}: '}" />
<TextBlock Text="{Binding GroupName}" />
<TextBlock Text="{Binding Strings[UnitPrice], Source={StaticResource LanguageResources}, StringFormat=', {0}: '}" />
<TextBlock Text="{Binding UnitPrice}" />
</StackPanel>
</telerik:RadExpander.Header>
How can I do conditional formmatting of the header, if for example Product == null, then Header should not display anything?
Edit: This one works.
<StackPanel Orientation="Horizontal" DataContext="{Binding CurrentItem, ElementName=activityProductGrid}">
<i:Interaction.Triggers>
<ie:DataTrigger Binding="{Binding}" Value="{x:Null}">
<ie:ChangePropertyAction TargetObject="{RelativeSource={RelativeSource AncestorType=StackPanel}}" PropertyName="Visibility">
<ie:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ie:ChangePropertyAction.Value>
</ie:ChangePropertyAction>
</ie:DataTrigger>
</i:Interaction.Triggers>
<-- from this point the came code as above -->
</StackPanel>
Specifically for null, you can specify binding parameters for a replacement value, or one to display in case of binding errors:
<TextBlock
Text="{Binding Product.Name, TargetNullValue=(empty), FallbackValue=(error)}"/>
(Taken from the WPF Binding Cheatsheet)
EDIT: I noticed that you didn't just want a specific binding to have a different value, but the whole control to not be displayed. For this, you can use Styles and Triggers, binging a DataTrigger to your Product property, and setting Visibility if it's null.
<telerik:RadExpander.Header>
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding Product}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
</StackPanel>
</telerik:RadExpander.Header>

Binding a checkbox in a datagrid header

I have a datagrid where the first column contains a checkbox to let the user selects specific rows. I have added a checkbox in the datagrid column header to check or uncheck all the rows.
Is it possible to add this functionality only with binding in XAML (no checked event).
<sdk:DataGrid AutoGenerateColumns="False" Grid.Row="1" Name="grid" ItemsSource="{Binding myCollection, Mode=TwoWay}" >
<sdk:DataGrid.Columns>
<sdk:DataGridCheckBoxColumn Binding="{Binding myCollection.UserSelected, Mode=TwoWay}" >
<sdk:DataGridCheckBoxColumn.HeaderStyle>
<Style TargetType="sdk:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox x:Name="checkAll" IsThreeState="True"
IsChecked="{Binding myCollection.UserSelected, Mode=TwoWay, Converter={StaticResource threeStateConverter}}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</sdk:DataGridCheckBoxColumn.HeaderStyle>
</sdk:DataGridCheckBoxColumn>
<sdk:DataGridTextColumn Binding="{Binding Description}" Header="Chemin" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
I think there's something wrong with the "myCollection.UserSelected" part. ThreeStateConverter is a value converter that would return null when some items are selected, true when they are all selected, etc. but the Convert method is never called (even though the PropertyChanged event is raised when UserSelected is changed).
Any idea on how I can do it? Thank you.
Probably, you've solved the problem already, but nevertheless:
<navigation:Page.Resources>
<model:MyModel x:Key="Model"/>
</navigation:Page.Resources>
...
<data:DataGridTemplateColumn Width="Auto" >
<data:DataGridTemplateColumn.HeaderStyle>
<Style TargetType="datap:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox IsThreeState="True" Margin="2,0,-13,0" DataContext="{StaticResource Model}" IsChecked="{Binding Path=AllChecked, Mode=TwoWay}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</data:DataGridTemplateColumn.HeaderStyle>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Checked, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
MyModel class contains bool? property that implements logic of selection. It is also used as a `DataContext of page.
I must admit that this potentially has problems if we need to change model.
EDIT: I found another way:
<navigation:Page.DataContext>
<model:MyModel />
</navigation:Page.DataContext>
...
<data:DataGridTemplateColumn Width="Auto" >
<data:DataGridTemplateColumn.HeaderStyle>
<Style TargetType="datap:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox IsThreeState="True" Margin="2,0,-13,0" IsChecked="{Binding Path=DataContext.AllChecked, ElementName=LayoutRoot, Mode=TwoWay}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</data:DataGridTemplateColumn.HeaderStyle>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Checked, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
Here we bind to DataContext of root layout element (usually called LayoutRoot) which inherits its data context from page by default.

Resources