WPF Command Binding ItemsControl in Styles - wpf

I have a style in the Textboxstyles.xaml as following
<Style x:Key="EmptyItemsControlUsabilityDashboard2017Style" TargetType="ItemsControl">
<Style.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Image Height="12" Width="12" Source="/YoLo;component/Resources/Images/link.png" Margin="0,3,0,0" />
<TextBlock x:Name="EmptyCollectionTextBox" Text="{x:Static UsabilityDashboard2017Loc:DashboardUsability2017Resource.lblNumNotDefined}"
Style="{StaticResource UsabilityDashboard2017TextBoxStyle}"
HorizontalAlignment="Center"
Margin="5,25,0,25"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
and I have used it inside another Xaml file as following
<ItemsControl ItemsSource="{Binding YoLoViewModelsCollection}" Name="YoLoViewModelsItemSource" Style="{StaticResource EmptyItemsControlUsabilityDashboard2017Style}">
Now it shows a text box that the this collection is empty but how can I set command bindings on the text block named "EmptyCollectionTextBox" inside the style that user user click it executes a command?
I have already seen the custom commands but somehow they are not working.

There's actually a lot of stuff wrong with this code. First of all I have no idea what that trigger is supposed to be doing, it looks like the controltemplate will only be set if there are no elements in the list?
Secondly, it looks like you're trying to represent each element in the collection with an image and text, all in an ItemsControl. You don't do that by templating the entire control, you do it by templating the ItemTemplate. And you use a DataTemplate, not a ControlTemplate:
Now going back to your actual question, you want notification whenever the TextBlock is clicked on. There are a multitude of different ways to do this, but for this case you may as well replace that TextBlock with a Button, and then override the Template with a ControlTemplate that represents it as a TextBlock. This gives you the best of both worlds: from the GUI's perspective it's really still a TextBlock, but you still get all the button click notifications and Command handler etc:
<Image />
<Button Command="{Binding ClickedCommand}" Cursor="Hand">
<Button.Template>
<ControlTemplate>
<TextBlock Text="Text Binding Goes Here" />
</ControlTemplate>
</Button.Template>
</Button>
This particular example assumes that the items in your collection have a command handler called "ClickedCommand". In practice your handler might reside in the parent class (e.g. the main window's view model), in which case you'd need to give your main window a x:Name (e.g. "_this") and bind to that instead, passing the item in as the CommandParameter so it knows which one was clicked:
<Button Command="{Binding ElementName=_this, Path=DataContext.ClickedCommand}" CommandParameter="{Binding}" Cursor="Hand">
And then your main view model has a handler that looks something like this:
public ICommand ClickedCommand { get { return new RelayCommand<YourCollectionItemType>(OnClicked); } }
private void OnClicked(YourCollectionItemType item)
{
}

Related

WPF MVVM use Datatrigger to press button and perform a command

I'm super new to WPF and MVVM, and I need to execute a command commandToExec in my View as soon as my ViewModel sets a property propertyToSet to true. Right now, I have a Stackpanel that becomes visible when propertyToSet is set, and a button inside that Stackpanel that is bound to the command commandToExec, and a Datatrigger on that button that presses it if propertyToSet is set. Something that looks roughly like this:
<StackPanel Visibility="{Binding propertyToSet, Converter={StaticResource BooleanToVisibilityConverter}}">
<Button
Command="{Binding commandToExec}">
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding commandToExec, Converter={StaticResource BooleanToVisibilityConverter}}">
<Setter Property="Button.IsPressed" Value="True" /> //Trying to set button isPressed to true to trigger command
</DataTrigger>
</Style.Triggers>
</Style>
</Button>
</StackPanel>
This doesn't work, and seems unintuitive and clunky, so I'm sure there is a better way to do this. I don't know much about XAML, so I'm just ballparking this code, but I would like to do something like this:
<StackPanel Visibility="{Binding propertyToSet, Converter={StaticResource BooleanToVisibilityConverter}}">
<SomethingThatAutomaticallyDoesCommand Command="{Binding commandToExec}"/>
<StackPanel/>
So that as soon as StackPanel becomes visible via propertyToSet, the arbitrary object SomethingThatAutomaticallyDoesCommand does its job and runs the command.
Another hacky idea I had saw to just set a tag on the StackPanel that updates it based on propertyToSet, and somehow binding my command to TargetUpdated, as found [here][1].
Anyway, I hope that made sense. Thanks so much!
[1]: Can i call a function via DataTrigger (XAML)?

Strange border still resding on button; even after modifying the control template

I have a 'feedback' button which has this strange border:
So I searched online for some solutions and modified the control template, and I got this:
Control Template code:
<Button.Template>
<ControlTemplate TargetType="Button">
<ContentPresenter Content="{TemplateBinding Content}" />
</ControlTemplate>
</Button.Template>
So even after modifying the control template - I am getting a strange brown border. Help would be appreciated regarding this.
Button code:
<Button Grid.Row="3"
Grid.Column="2"
Grid.RowSpan="2"
Style="{StaticResource IconStyleBase}"
Name="Feedback_Button">
<Button.Template>
<ControlTemplate TargetType="Button">
<ContentPresenter Content="{TemplateBinding Content}" />
</ControlTemplate>
</Button.Template>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="218*" />
<RowDefinition Height="68*" />
</Grid.RowDefinitions>
<!--Icon-->
<Button Background="#3767B0"
Style="{StaticResource IconStyleContent}">
<!--Content-->
<Button.ContentTemplate>
<DataTemplate>
<Viewbox>
<TextBlock Padding="55"></TextBlock>
</Viewbox>
</DataTemplate>
</Button.ContentTemplate>
</Button>
<!--Icon Text-->
<Button Background="#FF2D5BA0"
Style="{StaticResource IconStyleSubBase}">
<!--Content-->
<Button.ContentTemplate>
<DataTemplate>
<Viewbox>
<TextBlock Padding="15">Feedback</TextBlock>
</Viewbox>
</DataTemplate>
</Button.ContentTemplate>
</Button>
</Grid>
</Button>
A DataTemplate defines the appearance of the the items that you set as Content of a button, but the button itself as a container has a default style and control template that defines how it looks like, along with its different states like mouse-over or pressed. That is where the border comes from.
You can try to create a style that sets the BorderThickness to 0 and apply it on each of your buttons. This approach works for control templates that bind the border thickness from their templated parent.
<Style x:Key="BorderlessButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="BorderThickness" Value="0"/>
</Style>
If this does not work or you want adapt the appearance of your buttons in detail, you have to extract and adapt the button style and control template.
Your custom control template does not work, because you did not apply it to the inner buttons and you should remove Content="{TemplateBinding Content}". Nevertheless, your button control template does not define any control states, so it will not be responsive at all.
You should copy the control template for Button from here, or extract it manually via Blend or Visual Studio. Then you can remove or the Border within it, change its thickness or color, so it will disappear. Moreover, you can adapt its various states to fit your desired style.
A notice on your design. It do not think that it is a good idea to nest buttons. Your control should either be a single button or a panel with two buttons in it, but that also only makes sense if they execute different actions in a related context, like split buttons do.

Triggers, commands not firing in custom ListBox's ItemTemplate

I've got a custom ListBox control with a style set up in my Themes/Generic.xaml. I then have a button in the ListBox's ItemTemplate, and it's Click event isn't firing and I've got no idea why. Same goes for the button's Commands (I'm confident the Command issue isn't DataContext related) and interaction triggers. While attempting to debug, I noticed that using the default ListBox instead of my own stopped the problem, but I need to use the custom control.
This is essentially what I've got (fluff removed for brevity). The button:
<controls:CustomListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Click=MyHandler/>
</DataTemplate>
</ListBox.ItemTemplate>
</controls:CustomListBox>
And the custom control's style in Themes/Generic:
<Style TargetType="{x:Type controls:CustomListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:CustomListBox}">
<Border>
<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How can I get this event to fire?
I think this should probably be a usercontrol rather than a custom control.
Are you really going to change the template out of this for something else?
If you use an event handler like that then how are you planning on using the delegate? It's a very inflexible way of working you're headed in.
You mentioned command, which is probably rather more like it.
If you use a button in an item template with a command bound like
<Button Command="{Binding RowCommand}"
Then the datacontext of that Button is the content of the row.
If you bind ItemsSource to a collection Items of ItemVM then it's looking in the ItemVM that is presented to that row.

Why does my MenuItem have an Icon when I have overridden the DataTemplate?

I have successfully implement a WPF menu where the top-level items are drawn as large buttons and the lower level items are drawn as standard menu items (see my previous questions here and here).
In my original attempt at this my lower-level item template (SubItemTemplate in the example below) contained an image and a textblock. The result was something that looked like a normal menu item with an empty Icon area and the image next to the text in the text part of the menu item. I was not expecting to see the icon area in the visual display since I thought that the entire visual display would be determined by the contents of my template. The top-level template (TopLevelItemTemplate) does not have any empty icon area visible.
When I removed my image from teh lower-level template and replaced it with a style-setter for the Icon property, I got the display that I wanted.
I do not understand how and why the Icon property exists on my lower-level item DataTemplate.
Here's my code. The property HasParent is used to distinguish menu items that are not top-level (that is, the ones that are drawn with the SubItemTemplate). The section I don't understand is the DataTrigger.
Why is there an Icon property available inside that trigger?
<UserControl.Resources>
<Image x:Key="MenuIconResource16" Height="16" Width="16" Source="{Binding Icon32}" x:Shared="False" />
<HierarchicalDataTemplate x:Key="TopLevelItemTemplate" ItemsSource="{Binding Children}">
<StackPanel VerticalAlignment="Bottom" Orientation="Vertical">
<Image Width="32" Height="32" VerticalAlignment="Center" Source="{Binding Icon32}" ToolTip="{Binding UserHint}" />
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="SubItemTemplate" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</UserControl.Resources>
<WrapPanel Height="Auto">
<Menu ItemsSource="{Binding DataContext.EventMenu.TopLevel, ElementName=UserControl}" ItemTemplateSelector="{StaticResource MenuItemTemplateSelector}">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="CommandParameter" Value="{Binding EventType}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasParent}" Value="true">
<Setter Property="Icon" Value="{StaticResource MenuIconResource16}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.ItemContainerStyle>
</Menu>
</WrapPanel>
I thought that the entire visual display would be determined by the contents of my template.
#dkozl noted the difference between DataTemplate and Template -- that is the important distinction. A data template is a XAML fragment that the owning control uses as part of the overall control, which may or may not include other (customizable or hard-coded) visual elements, and/or other data templates. The control template is where this visual structure of the control is defined. If you set/override a control template, then your expectation of not seeing any other visual content, will hold true.
The top-level template (TopLevelItemTemplate) does not have any empty icon area visible.
The other thing to note here is that the default style for Menu defines multiple control templates for its MenuItems. These templates are applied depending on the role "TopLevelHeader", "TopLevelItem", "SubmenuHeader", and "SubmenuItem". So you will see different behavior for these different menu items. Take a look at the default styles/templates, which should be illuminating (although they are kind of complex).
Why is there an Icon property available inside that trigger?
A style trigger has the capability of modifying any dependency property of the control it is applied to. Since the style trigger in question is being applied to the MenuItem control, it can modify the Icon dependency property, which is owned by that control.

Custom WPF tooltip

I want to create a WPF tooltip containing a label for the header of the tooltip and then a textblock containing more detailed text. I've created the following style in a resource dictionary:
<Style x:Key="AppToolTip"
TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<StackPanel>
<Label Content="{TemplateBinding Content}" FontWeight="Bold" Background="Blue" Foreground="White">
</Label>
<TextBlock Padding="10" TextWrapping="WrapWithOverflow" Width="200">
</TextBlock>
</StackPanel>
</ControlTemplate>
</Setter.Value></Setter>
</Style>
And can successfully apply this style to a button like so and have the tooltip header appear:
<Button.ToolTip>
<ToolTip Style="{DynamicResource PalletToolTip}">
<Binding Source="{x:Static ResStrings.New}"/>
</ToolTip>
</Button.ToolTip>
What i'm stuck on is how can i set the content of the extra descriptive text from the usage above ? I'm already data binding to the Content property when showing the tooltip header.
Anyone who's read Adam Nathan's WPF Unleashed book will recognise that i'm using his example tooltip XAML but in his case, he's used hard coded strings for the content of the label and textblock. I want to create something that's more reusable and hence want to use data binding to achieve the same effect.
I would inherit a HeaderedToolTip class from ToolTip and add a Header property. I would specify the template for that control much as you've done. Then I would be able to use it like so:
<Button>
<Button.ToolTip>
<HeaderedToolTip Header="My Title" Content="My Content"/>
</Button.ToolTip>
</Button>
Or, with bindings:
<Button>
<Button.ToolTip>
<HeaderedToolTip Header="{Binding ToolTipTitle}" Content="{Binding ToolTipText}"/>
</Button.ToolTip>
</Button>
You can use an object or ViewModel that contains all the necessary properties you need in the tooltip.
class MyToolTipViewModel : INotifyPropertyChanged
{
public string Header
{
get{ return mHeader;}
set{ mHeader = value; RaisePropertyChanged("Header"); }
}
public void RaisePropertyChanged(string aProperty)
{
// .. implementation of INotifyPropertyChanged
}
}
then you can set the tolltip directly on an instance of this class.
myButton.ToolTip = new MyToolTipViewModel();
now after that, your tooltip will just show the full qualified name of the ViewModel class.
What you need now is a DataTemplate, which tells WPF how to convert the class into visual object.
<DataTemplate DataType="{x:Type MyToolTipViewModel}">
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
The DataTemplate need to be placed in the resource tree. In the resource section of a higher level object or directly on the application or window resources level.
Hope that helps.

Resources