WPF: How to set bool to true from DataTemplate for Button? - wpf

I know it is possible to do this by working with ICommand, but since this is the only place in my whole project where this is needed, I am asking myself, if there is maybe a better solution than implementing RelayCommand and ICommand and an additional method in my otherwise property-only class?
Maybe my scenario may help here:
I have a ListView which is bound to a list of properties (this is a custom-class I made to display those).
I have a button in eacht row of entries, that I want to set the "IsToDelete"-Property to true.
If "IsToDelete" is true, all the controls I show in my ListView will collapse.
The whole logic is up to this point in DataTemplates, the one for my button looks like this:
<DataTemplate x:Key="DeleteButtonTemplate">
<Button Name="bt_Delete"
Command="{Binding RelativeSource={RelativeSource AncestorType=ListView}, Path=ItemSource.DeleteClicked}">
<Image Height="16" Width="16" Source="Images\RecycleBin\VSO_RecycleBin_16x.png"/>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsToDelete}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
Note the command in this DataTemplate is currently not working as intended, that is why I even got to the question ;)
In the optimal case I'd just have to do something like:
<Setter Property="{Binding IsToDelete}" Value="True"/>
So, is there a way to solve it like this or at least in a less complicated way than ICommand with RelayCommand?

why not use ToggleButton and bind IsChecked property to IsToDelete property of a viewModel?
simplified example with ItemsControl
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ToggleButton Content="X" IsChecked="{Binding Path=IsToDelete}"/>
<Border Width="100"
Background="MediumPurple"
Margin="5">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsToDelete}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

You could use the Click eventhandler of the Button. Then you don't need any Commands.
private void bt_Delete_Click(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
YourClass dc = btn.DataContext as YourClass;
dc.IsToDelete = true;
}
I don't really like this solution, but I think, it would work.

Related

WPF How to attach Popup to simple UIElement like rectangle

It took me hours to figure out the answer to this question, so I thought I would write an FAQ or answer for what I found. (it is based on the following thread Binding Textbox IsFocused to Popup IsOpen plus additional conditions)
I found lots of examples of binding popups to things like toggle buttons and other things that are based on windows chrome and have built in triggers. But in my application I wanted to bind a popup to a simple rectangle with a custom brush fill. I could not find an example on how to have a popup open ans stay open when a user mouses over the rectangle.
So I am posting this question and I will immediately post the answer I found so that hopefully someone else can benefit from it. I will also mark an answer for anyone who can help me understand if stackoverflow allows posts like this, or a better way I could have gone about it.
EDIT 1)
I can't self answer for 8 hours so here is the working code:
the following is a simple example of how to use the popup on a basic UIElement like a rectangle/ellipse/etc...
<Grid HorizontalAlignment="Stretch" Height="Auto">
<Rectangle x:Name="PopupRec"
Grid.Row="0"
Width="20" Height="20"
HorizontalAlignment="Right"
Fill="Gray" Margin="0,0,0,10" />
<Popup x:Name="SortPopup"
PlacementTarget="{Binding ElementName=PopupRec}"
StaysOpen="False"
PopupAnimation="Slide"
AllowsTransparency="True">
<Border Background="White" Padding="15">
<StackPanel Orientation="Vertical">
<Button Command="{Binding MyCommand}" CommandParameter="5">5</Button>
<Button Command="{Binding MyCommand}" CommandParameter="10">10</Button>
<Button Command="{Binding MyCommand}" CommandParameter="15">15</Button>
<Button Command="{Binding MyCommand}" CommandParameter="20">20</Button>
</StackPanel>
</Border>
<Popup.Style>
<Style TargetType="{x:Type Popup}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=PopupRec, Path=IsMouseOver}" Value="True">
<Setter Property="IsOpen" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=SortPopup, Path=IsMouseOver}" Value="True">
<Setter Property="IsOpen" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
</Popup>
</Grid>
Just paste this inside window/usercontrol/etc...
I'd suggest the following improvement. It would make the Popup Style independent of any element names, and would thus enable you to use it as a default Style by putting it into the Window's or UserControl's Resources.
<Style TargetType="{x:Type Popup}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsMouseOver,
RelativeSource={RelativeSource Self}}"
Value="True">
<Setter Property="IsOpen" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=PlacementTarget.IsMouseOver,
RelativeSource={RelativeSource Self}}"
Value="True">
<Setter Property="IsOpen" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
And please note that a Rectangle is not a "basic UIElement". It's a Shape, which itself is a FrameworkElement.

Datatrigger on contentpresenter.content not working

I am trying to switch the content of a contentpresenter based on a datatrigger.I want to display a usercontrol in the contentpresenter.content, if i have a value set or else i need to display an error message.But the binding on my datatrigger fails stating that the property is not found.I cant get the datacontext to inherit for the datatrigger checking.I can make it work by using the commented out code.But i am confused why it doesn't work the normal way.
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="Content" Value="{Binding UC}"/>
<Style.Triggers>
<!--<DataTrigger Binding="{Binding DataContext.HasValue,RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}" Value="false">
<Setter Property="Content" Value="No preview"/>
</DataTrigger>-->
<DataTrigger Binding="{Binding HasValue}" Value="false">
<Setter Property="Content" Value="No value"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
If you want to use triggers to display UserControl, you should use ContentControl not ContentPresenter.
I prefer to use ContentPresenter for CustomControls and When I am using the UserControl for views of Custom Data Types in my system and Allow to give dynamic behavior.
Example: To switch templates for ContentPresenter you need to set ContentTemplateSelector like this
<ContentPresenter Content="{Binding MyContent}"
ContentTemplate="{Binding MyContentTemplate}"
ContentTemplateSelector="{Binding MyContentTemplateSelector}"/>
MyContent, MyContentTemplate & MyContentTemplateSelector are Dependency Properties and can be binded wherever you are using its instance.
READ :
Usage of ContentPresenter
What is the difference between ContentControl and ContentPresenter
The binding mentioned in the question won't work as
ContentPresenter’s DataContext is automatically set to the value of
its Content property, while ContentControl’s DataContext is not.
Bindings are resolved relatively to the value of the DataContext property. If you declare a binding on the ContentPresenter, the moment its content is set, the binding would be re-evaluated.
ContentControl.Content Property can be changed on any trigger based on your requirement. If you want to use it to change on PropertyChanged Event of a property of ViewModel, DataTrigger can be used by binding it with a DataTemplate with UserControl instance in it or using static resource of that UserControl.
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Value="{StaticResource UnSelectedDataTemplate}" Property="ContentTemplate" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected}" Value="True">
<Setter Value="{StaticResource SelectedDataTemplate}" Property="ContentTemplate" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentContro.Style>
</ContentControl>
READ How to use triggers for content template, more details here
Difference in DataTemplate and StaticResource scope is DataTemplate creates a new instance of the template every time its applied.
Whereas, StaticResource is using the same instance of UserControl again (Static Instance).
You can also use EventTriggers to change content base don Control Events like MouseOver etc.
Alternate approach
Very similar to the above with slight difference. Defining as a data template in resources. Triggering for the content change is essentially identical.
...in <x.Resources /> tag:
<DataTemplate x:Key="DesignerTemplate" DataType="{x:Type vm:SolutionViewModel}">
<vw:SolutionDesignerView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SolutionViewModel}">
<ContentControl Content="{Binding }">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoaded}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource DesignerTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
...then:
<ContentControl Content="{Binding Solution}" />
I usually use trigger Like this...
<UserControl>
<UserControl.Resources>
<DataTemplate x:Key="normalTemplate" >
<!-Fav UserControl->
</DataTemplate >
<DataTemplate x:Key="overWriteTempalte">
<!-Fav UserControl-> </DataTemplate>
</UserControl.Resources>
<ContentPresenter x:Name="ContentField"
Content="{Binding}"
ContentTemplate="{StaticResource ResourceKey=normalTemplate}" />
<UserControl.Triggers>
<DataTrigger Binding="{Binding Path=MyProperty}" Value="True">
<Setter TargetName="ContentField" Property="ContentTemplate" Value="{StaticResource ResourceKey=overWriteTempalte}" />
</DataTrigger>
</UserControl.Triggers>
</UserControl>
If Bindings are a problem Use Snoop to Detect binding errors

Change ListView.ItemTemplate on subelement change

let's say we have simple data class:
public class Ex {
public string Prop1 {...} // notify property
public string Prop2 {...} // notify property
}
and an ObservableCollection of objects of this class. I want to have this collection displayed in a ListView with seperated DataTemplated which is distinguished by Ex.Prop2 (if it is null or empty then template01 is used, otherwise template02). This property can be changed in runtime so simple "trick" with ListView.ItemTemplateSelector does not work :(
How to achieve this functionality? Is it possible to achieve it any other way than listening to NotifyPropertyChanged on each object of the collection and than changing manually the template?
Thanks for your help.
Below piece of code which I already have:
<ListView x:Name="lstTerms"
ItemsSource="{Binding Game.Words}"
HorizontalContentAlignment="Stretch"
Grid.IsSharedSizeScope="True">
<ListView.ItemContainerStyle>
<Style>
<Setter Property="Control.Padding" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<!-- checks if element is null or its Prop2 is null or empty. If so, uses NullTemplate -->
<ListView.ItemTemplateSelector>
<local:MySelectTemplate
NormalTemplate="{StaticResource NormalItemTemplate}"
NullTemplate="{StaticResource NullItemTemplate}" />
</ListView.ItemTemplateSelector>
</ListView>
Instead of using a TemplateSelector, you can have a single DataTemplate containing two Grids, which switch visibility dependent on the property values.
Here is an example:
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid Background="LightBlue" Name="normalGrid">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Prop1}" Value="{x:Null}">
<Setter Property="Grid.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="{Binding Prop1}"></TextBlock>
</Grid>
<Grid Background="Green" Name="nullGrid">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=normalGrid, Path=Visibility}" Value="Visible">
<Setter Property="Grid.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="{Binding Prop2}"></TextBlock>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Obviously you could replace the TextBlock elements with UserControls representing your two DataTemplates.
If you want, you can also remove the need for the bulky Styles by binding Grid.Visibility to a property (named, for example, IsVisible) on your ViewModel and using a VisibilityConverter.
I usually just use a ContentControl which changes its ContentTemplate based on a DataTrigger. DataTriggers respond to the value getting changed, while DataTemplateSelectors do not
<Style x:Key="SomeStyleKey" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Prop2}" Value="{x:Null}">
<Setter Property="ContentTemplate" Value="{StaticResource NullTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Prop2}" Value="">
<Setter Property="ContentTemplate" Value="{StaticResource NullTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
...
<ListView.ItemTemplate>
<DataTemplate>
<ContentControl Style="{StaticResource SomeStyleKey}" />
</DataTemplate>
</ListView.ItemTemplate>
You could also use a Converter that returns String.IsNullOrEmpty(value) if you wanted a single DataTrigger

How to set focus in a textbox when user click an expander?

I have an expander...and a textbox. The textbox is hidden by default. when user click on the expander, I am displaying the text box. This is working fine.
What I need is, when user click on the expander...I need to set the focus in the textbox.
Please help me to do this...I tried with following code...but it seems "IsFocused" is readonly property.
Any help would be appreciated !
<StackPanel>
<DockPanel>
<TextBlock DockPanel.Dock="Left" Text="ID"/>
<Expander x:Name="ID" DockPanel.Dock="Right" IsExpanded="False" ExpandDirection="Down">
</Expander>
</DockPanel>
<TextBox Text="{Binding Path=SearchCCCId.Value,UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=ID,Path=IsExpanded,Converter={x:Static local:Converters.BoolToVisibility}}" Width="70" >
<TextBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ID, Path=IsEpanded}" Value="True" >
<Setter Property="IsFocused" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
With all your help, I am able to find out the answer...
<TextBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ID, Path=IsExpanded}" Value="True">
<Setter Property="FocusManager.FocusedElement"
Value="{Binding ElementName=PropertyIDSearch}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
IsFocused is ReadOnly property. You may want to look at these -
How to set focus to textbox using MVVM?
WPF - Set Focus when a button is clicked - No Code Behind
You can handler Expanded event of Expander and set focus inside handler method.
private void OnExpanderExpanded(object sender, RoutedEventArgs args)
{
txtTest.Focus();
}

WPF ComboBox that shows nothing selected when disabled (IsEnabled == false)

I'm thinking out different ways to have a WPF ComboBox show blank as if nothing is selected when IsEnabled is set to false. Like always I'm trying to do this without having to redefine the whole control template for the ComboBox which is always a struggle I have with WPF. If anybody has any solutions more elegant than redefining the whole ComboBox control template please let me know.
The reason for what I'm trying to do is I have a CheckBox that represents an "All" option and when checked it disables the ComboBox which is used to pick only a single individual item. If my CheckBox is checked it is sometimes confusing to the users to see a value remaining in the ComboBox since that value has no meaning in that state of the UI.
Another requirement is that the solution cannot modify the SelectedValue, SelectedIndex, or SelectedItem values of the ComboBox since I would like to retain the previuosly selected item in the case that the users unchecks the "All" CheckBox.
Solution based on HCL's answer:
<ComboBox IsEnabled="{Binding ElementName=myCheckBox, Path=IsChecked}"
ItemsSource="{Binding Path=MyItems}"
SelectedValue="{Binding Path=MySelectedItem}">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentControl x:Name="content" Content="{Binding MyItemDescription}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ComboBox}, Path=IsEnabled}"
Value="False">
<Setter TargetName="content"
Property="Visibility"
Value="Hidden" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You can do something with triggers:
Try setting the ItemTemplate to an empty DataTemplate when the box is disabled. This will affect the rendering of the selected item and therefore hide it.
Another simple but not very nice solution would be to set the foreground color to the same as a background color.
I believe you can do this with a Style, rather than redefining the control template. Use a Trigger on the IsEnabled property to set the text shown in the ComboBox. Altering the SelectedItem would be my first approach, but since you don't want to do that, you may find success setting the DisplayMemberPath. Something like this (untested)...
<Style TargetType="{x:Type ComboBox}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Trigger.Setters>
<Setter Property="DisplayMemberPath" Value="{x:Null}"/>
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
Here's a style that does what you want. It employs a technique that I use all the time: a grid that contains multiple versions of the control, and data triggers that ensure that only one version is visible at any one time.
<ComboBox.Style>
<Style TargetType="ComboBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<DockPanel>
<CheckBox x:Name="IsActive" DockPanel.Dock="Left"/>
<Grid>
<ComboBox
ItemsSource="{TemplateBinding ItemsSource}"
SelectedItem="{TemplateBinding SelectedItem}"
SelectedIndex="{TemplateBinding SelectedIndex}"
SelectedValue="{TemplateBinding SelectedValue}">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=IsActive, Path=IsChecked}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
<ComboBox>
<ComboBox.Style>
<Style TargetType="ComboBox">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=IsActive, Path=IsChecked}" Value="False">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</Grid>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.Style>
This preserves the selected item, selected index, and selected value, just as you want. In fact, it does this a little too well; there's not actually a way of telling that the user deactivated the combo box, since there's no property on ComboBox that exposes this information. I'd probably actually implement this as a custom control derived from ComboBox that exposed the value of the check box as an IsActive property. There are lots of other ways to do it.

Resources