WPF ComboBox Show Alternate Text - wpf

I am trying to change the text of a WPF combobox button to something custom that isn't an actual selected item. The combobox control is setup with a custom datatemplate that allows it to contain checkboxes and what I'm trying to do is display "None Selected", [SelectedItem.Text] or "Multiple Selected" depending on whether zero items are checked, one is checked or more than one is checked. I found one solution on here that involved adding a new textblock instance that could display this text and then set the text of it to what I want to display. This works great until someone clicks and area next to the label of the checkbox and the text of that item shows up underneath my custom textblock causing weird overlap issues.
My assumption is a converter of some kind (which can replace the custom textblock as far as I'm concerned - no preference there), but I'm not entirely sure how to apply it. Here is my XAML thus far:
<Grid Grid.Row="4" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ComboBox x:Name="SubjectMatterList" Style="{StaticResource ComboBox}" ItemsSource="{Binding SubjectMatters}" Visibility="{Binding AdjunctListVisibility}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" MinWidth="125" MaxWidth="125" Margin="6">
<CheckBox Content="{Binding Name}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<mvvm:EventToCommand Command="{Binding DataContext.SubjectMatterSelectedCommand, ElementName=GradeLevelList}" CommandParameter="{Binding}" />
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<mvvm:EventToCommand Command="{Binding DataContext.SubjectMatterDeselectedCommand, ElementName=GradeLevelList}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock IsHitTestVisible="False" x:Name="SelectedSubjectMatter" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10" Padding="10,6,0,6" Text="{Binding ComboBoxSubjectMatterText}" Visibility="{Binding AdjunctListVisibility}" />
<TextBlock Text="{Binding SelectedSubjectMatterText}" Grid.Row="1" Margin="10" Visibility="{Binding SubjectMatterSelectedVisibility}" />

http://blogs.microsoft.co.il/blogs/justguy/archive/2009/01/19/wpf-combobox-with-checkboxes-as-items-it-will-even-update-on-the-fly.aspx

Related

WPF Mouse Binding on Listbox Item Template Inconsistent

I have a listbox which binds to an observable collection of a custom type which displays each item through my data template:
<ListBox x:Name="ListBox" Style="{StaticResource CustomListBox}" ItemsSource="{Binding HandStats}" Height="410" Width="150" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding HoleCards[0].CardParts.Value}" Margin="2"/>
<Image Source="{Binding HoleCards[0].SuitImagePath}" Width="10" Height="10" Margin="2"/>
<TextBlock Text="{Binding HoleCards[1].CardParts.Value}" Margin="2"/>
<Image Source="{Binding HoleCards[1].SuitImagePath}" Width="10" Height="10" Margin="2"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Runs:" Margin="2"/>
<TextBlock Text="{Binding NumberOfRuns}" Margin="2"/>
<TextBlock Text="Players:" Margin="2"/>
<TextBlock Text="{Binding NumberOfPlayers}" Margin="2"/>
</StackPanel>
<StackPanel.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding Path=DataContext.PopulateReport, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding ElementName=ListBox, Path=SelectedItem}"/>
</StackPanel.InputBindings>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Image of my listbox:
http://imgur.com/a/QGz8z
Now ideally when I click anywhere in the stack panel for each item, the item will be selected and the command will be fired. But my problem is that the item selection and the command firing is not working as it should.
The item will only become selected if I click in between the text blocks or in the empty space to the right of the text blocks, but the command will not be fired. The command will only fire once I have firstly selected the item, and then click again onto one of the text blocks or one of the images.
I'm still quite new to WPF so if I'm not making much sense please let me know, thanks in advance :)
So instead of using input bindings I decided to use a different method. I created a property in my vm and bound this to the selected item of my list. So now every time I select a new item (which works perfectly now) the property is updated in my vm. Simple!
Try to set the Background property of the StackPanel to Transparent:
<StackPanel Orientation="Vertical" Background="Transparent">
...
If it doesn't have a Background the click events won't be raised as expected.

Silverlight: How to set a ListBoxItem as selected only when a child control is clicked?

In a Silveright view, I have a ListBox, and each ListBoxItem within it contains a Hyperlink. By default, clicking anywhere on a ListBoxItem will highlight that item as selected. What I'd like to do is override the default selection behaviour, and make the Hyperlink inside each ListBoxItem act as the selection 'handle' for its parent, so that the ListBoxItem is only selected when the Hyperlink is clicked, while clicking anywhere else on the ListBoxItem has no effect. Is this possible and if so how could I achieve it?
The ItemTemplate of the ListBox:
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="LightGray" BorderThickness="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".65*"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="1" Margin="5,0,0,0"
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="{Binding UploadDate,
Converter={StaticResource DateDescriptionConverter}}"/>
<TextBlock Grid.Column="1" Grid.Row="0" Margin="5,0,0,0"
Text="{Binding HistoryStatus}"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
<HyperlinkButton x:Name="DisplayDocument"
CommandParameter="{Binding}"
Grid.Column="2" Grid.Row="1" Content="View"
HorizontalContentAlignment="Left"
VerticalAlignment="Center" Margin="4">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="Click">
<Interactions:CallMethodAction
TargetObject="{Binding Path=DataContext,
ElementName=DocumentViewPanel}"
MethodName="ViewDocumentButtonClick"/>
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</HyperlinkButton>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
To put this in some context, the contents of the ListBox is a list of documents, and clicking the Hyperlink loads the document in to a separate pane. With the default selection behaviour, by clicking the background of an item in the ListBox, the item can display as being selected without its corresponding document actually being loaded.
Thanks in advance for any answers.
I recommend you pick another control from the range of available base controls: I think the ItemsControl would be a good fit.
Why?
The ListBox is made to display a list of items and whenever you click anywhere on an item it will be selected. That's its purpose.
Apparently you don't want that - I'm not even sure you want the concept of "selection" at all.
As far as I understood: You want a visual list of items; each offering at least one action you can invoke for that very item and you want a visual indication that you have performed an associated action for an item.
Let's try to do this with an ItemsControl:
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
....
<Border
Background="Blue"
x:Name="DocumentDisplayed_Indicator"
Opacity="0"/>
<HyperlinkButton ...>
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="Click">
<Interactions:CallMethodAction
TargetObject="{Binding Path=DataContext,
ElementName=DocumentViewPanel}"
MethodName="ViewDocumentButtonClick"/>
<SetPropertyAction
TargetName="DocumentDisplayed_Indicator"
Property="Opacity" Value="1"/>
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</HyperlinkButton>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Wpf Expander Collection, how to have parent ViewModel know which Expander object current?

In a view which contains an item having a collection of child items, I have an ItemsControl which hosts the child items collection. The individual items are contained in an Expander. When a child item is expanded, I need the parent view model to be aware of which child is being acted upon. I was able to implement an Event Trigger which passes the child object as a parameter to the parent view model, and the parent view model can then set a SelectedChildObject property. That is what I need but where it falls short is when multiple items are expanded, and the user acts on an item which is not in the most recently expanded item. When this happens the item they are interacting with does not match the SelectedChildObject property, since only the most recently expanded object would be the property value.
I have seen the solutions which use a ListBox to contain the Expander, and then set the Expander IsExpanded based on the ListBox IsSelected, but I don't like this solution because it only allows one expander open at a time, plus it does not seem possible to have the Expander stretch to fill all of the space in the ListBox, and this look does not look good.
Is there a way I can always let the parent view model know which child object is being acted upon?
<ItemsControl Grid.Row="0" Grid.Column="0"
ItemsSource="{Binding Slots, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Padding="10">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Expanded">
<i:InvokeCommandAction Command="{Binding DataContext.ExpandedCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.Column="0"
Height="16" Width="16" Source="/WpfApp1;component/Assets/Images/Warning.ico" />
<StackPanel Grid.Row="0" Grid.Column="1"
Orientation="Horizontal">
<Label Content="Slot: " />
<Label Content="{Binding SlotNumber, Mode=TwoWay, ValidatesOnNotifyDataErrors=False}" />
</StackPanel>
</Grid>
</Expander.Header>
<StackPanel Margin="20">
<StackPanel Orientation="Horizontal">
<Label Content="Slot Number:" Margin="0 5 2 0" Width="100" />
<TextBox Text="{Binding SlotNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}"
Style="{DynamicResource Configuration.Input.TextBox}"
Width="30" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Modules:" Margin="0 5 2 0" Width="100" />
<ListBox ItemsSource="{Binding Source={x:Static local:SlotsViewModel.AllowedModules}}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}"
Command="{Binding DataContext.AddRemoveAllowedModuleCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</StackPanel>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
What kind of actions are you trying to perform on Slots?
Something like removing the last manipulated slot? If so then you will probably have to write a behavior or a control that will capture PreviewMouseDown events and send a command/event to notify your parent View Model. But it's not ideal.
And any way you will probably want the "Selected slot" to be highlighted befor it can be removed. You could also try to remove background of the expander and use the ListBox. So that when you click on the background of your Slot it selects and highlights the ListBoxItem. In this way you can bind SelectedItem to the VM.

WPF Textboxes and Textblocks not rendered

I have a strange behavior of my application on some computers (it only happens at some customer pcs, i can't reproduce it on my machines).
I have an application to enter and view production scrap data. Every column is an control on it's own. The Expanders are accordion Controls from the WPF Toolkit.
Sometimes Textblocks are not visible:
When the bug happens to Textboxes, they are not even consuming any space:
This is the Datatemplate for the Content of the Accordion Item:
<DataTemplate x:Key="YieldAccContent" DataType="Models:Cell">
<DataTemplate.Resources>
<DataTemplate x:Key="It1">
<StackPanel >
<Label BorderBrush="{x:Static SystemColors.ControlLightBrush}" Padding="1" Height="22" BorderThickness="1" Content="{Binding Value}" Visibility="{Binding Cell.CellGroup.ParentMeasurement.IsEnabled, Converter ={StaticResource boolTovisinv}}"/>
<controls:NumberTextbox Text="{Binding Value, Converter ={StaticResource EmptyStringToNullConverter}, UpdateSourceTrigger=PropertyChanged}" Height="22" Width="80" Visibility="{Binding IsEnabledAndBad, Converter ={StaticResource boolTovis}}" Background="AliceBlue">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus" >
<cmd:EventToCommand Command="{Binding MeasurmentViewModel.LostFocusCommand, Mode=TwoWay, Source={StaticResource Locator}}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</controls:NumberTextbox>
<toolkit:MaskedTextBox Mask ="-99999999" Value="{Binding Value, Converter ={StaticResource EmptyStringToNullConverter}, UpdateSourceTrigger=PropertyChanged}" PromptChar=" " Height="22" Width="80" Visibility="{Binding IsEnabledAndGood, Converter ={StaticResource boolTovis}}" IncludeLiterals="True" IncludePrompt="False" Background="AliceBlue" ValueType="{x:Type sys:Int64}" GotFocus="MaskedTextBox_GotFocus_1" SelectionChanged="MaskedTextBox_SelectionChanged_1">
</toolkit:MaskedTextBox>
</StackPanel>
</DataTemplate>
</DataTemplate.Resources>
<StackPanel >
<Label BorderBrush="{x:Static SystemColors.ControlLightBrush}" BorderThickness="1" Padding="1" Height="22" Width="160" Content="{Binding YieldInput}" Background="Moccasin"/>
<Label BorderBrush="{x:Static SystemColors.ControlLightBrush}" BorderThickness="1" Padding="1" Height="22" Width="160" Content="{Binding YieldOutput}" Background="Moccasin"/>
<Label BorderBrush="{x:Static SystemColors.ControlLightBrush}" BorderThickness="1" Padding="1" Height="22" Width="160" Content="{Binding Yield}" Background="Moccasin"/>
<ItemsControl ItemsSource="{Binding Stations}" ItemTemplate="{StaticResource It1}">
</ItemsControl>
</StackPanel>
</DataTemplate>
This is the Accordion Item itself:
<System_Windows_Controls:Accordion ContentTemplate="{StaticResource Cont}" ItemTemplate="{StaticResource Head}" ItemsSource="{Binding Cells}" SelectedIndex="{Binding SelectedAccItem, Mode=TwoWay}" SelectionMode="{Binding MeasurmentViewModel.SelectionMode, Source={StaticResource Locator}}" HorizontalAlignment="Stretch" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemsChanged" >
<cmd:EventToCommand Command="{Binding MeasurmentViewModel.SelectedItemsChangedCommand, Mode=TwoWay, Source={StaticResource Locator}}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</System_Windows_Controls:Accordion>
What could be the cause of this behavior?
Going by your description about the missing TextBoxes not taking any space, I can only assume that you have a Trigger or Converter attached to their Visibility property. If you put a break point in the Converter that is attached to the offending TextBox, then you can debug whether this is the cause quite easily. I assume that the bool value(s) that you are binding to in the Visibility property is not being set correctly... please check whether this is true.

WPF MVVM customizing ListView cell to insert hyperlink and change text color

I have a GridView cell in a ListView that is defined as a TextBlock and bound to a string on my ViewModel. I want to be able to change parts of the text into hyperlinks and part into different colors programatically.
Here is the XAML for the GridView cell:
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="#dfdfdf" HorizontalAlignment="Stretch">
<TextBlock Text="{Binding Data}" Margin="3"/>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
And here's what it looks like at the moment:
An example of the text that is bound would be:
<color:#ff0000>Test item</color>
Test item 2
<link:http://www.google.com>Test hyperlink</link>
I have no problem with the regex to parse the bound text and pull out the required information but how would I go about changing the TextBlock into different colors and add a hyperlink?
Thanks in advance
You can put multiple Run elements inside a TextBlock and style then however you like.
Here is an example with a working hyperlink which even supports MVVM :)
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="#dfdfdf" HorizontalAlignment="Stretch">
<TextBlock Margin="3">
<Run Text="{Binding Data}" />
<Run Text="Some more data" Background="Red" />
<Run Text="Click Me" Foreground="Blue" TextDecorations="Underline" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<local:EventToCommand Command="{Binding LinkClickCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Run>
</TextBlock>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
Note:
The Interaction.Triggers are from System.Windows.Interactivity and the EventToCommand from MVVMLight.
Similar to Blachshma's answer, but using a hyperlink right in there with the Run objects:
<TextBlock>
<Run Text="{Binding Data}" />
<Run Text="Some more data" Background="Red" />
<Hyperlink Command="{Binding Path=Command}">
<TextBlock Text="{Binding Path=Text}"/>
</Hyperlink>
</TextBlock>
Clearly I have a Command and Text properties to bind to for the hyperlink. Will look exactly the same, but will also respond to the command's CanExecute predicate (hyperlinks go grey).

Resources