Triggers, commands not firing in custom ListBox's ItemTemplate - wpf

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.

Related

WPF Command Binding ItemsControl in Styles

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)
{
}

CustomUserControl Double Click Event in ControlTemplate in WPF

In WPF I created a CustomUserControl with DoubleClick event handler. I am using this control inside ControlTemplate in this way:
<xcdg:DataCell FieldName="." Template="{StaticResource myTemplate}">
</xcdg:DataCell>
And also this my ControlTemplate:
<ControlTemplate x:Key="myTemplate" TargetType="xcdg:DataCell">
<uicontrols:MyCustomControl Tag="{Binding ID}" Margin="0" Height="140" Width="150" DoubleClick="ctrl_DoubleClick">
</ControlTemplate>
DoubleClick event handler is not working inside the ControlTemplate. what is the problem and what is the best solution?
I found out my answer. rather than using extra template in DateCell. I used inline template.
<xcdg:DataCell FieldName=".">
<xcdg:DataCell.Template>
<ControlTemplate>
// your template code. here the event of my controls are working.
</ControlTemplate>
</xcdg:DataCell.Template>
</xcdg:DataCell>

UserControl within a ControlTemplate

I have a ControlTemplate for a Telerik Tile and I am overriding like below:
<ControlTemplate TargetType="{x:Type ctrl:Tile}">
<Border>
<local:UserControl>
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
</local:UserControl>
</Border>
</ControlTemplate>
My user control looks like:
<DockPanel>
<!-- some content -->
<ContentPresenter/>
</DockPanel>
The ControlTemplate does not display the content of the UserControl.
If I change my control template to:
<ControlTemplate TargetType="{x:Type ctrl:Tile}">
<Border>
<StackPanel>
<local:UserControl/>
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
</StackPanel>
</Border>
</ControlTemplate>
It will find the content and place it appropriately. It seems like the ControlTemplate cannot find the content once it's nested inside my UserControl. Is there anything I could be doing wrong?
Note that these ControlTemplate Items are appearing in an ItemsPresenter.
You're treating the UserControl as if it is a basic ContentControl (like a Button) which is a little different than what it actually is. Using Button as an example, when you add a child (i.e. a TextBlock) into a Button element that's actually setting that TextBlock as the Button's Content property. The way it gets rendered is through the Button's ControlTemplate, which includes a ContentPresenter to inject Content into. The Visual Tree ends up like this:
<Button>
-start Template
<Border>
<ContentPresenter>
-start Content
<TextBlock>
So far that's basically the model your code is following. The problem is that you're using a (still ContentControl derived) UserControl instead, which rather than using a ControlTemplate is most often defined with a XAML+code-behind model, where the XAML defines the Content of the UserControl. (It is possible to switch these models and template a UserControl or make a Button derived class with XAML+code-behind but not common)
If you want to both define the look of your UserControl in XAML as normal and still be able to inject other content you can add another DependencyProperty that mirrors the setup of the Content property and set your content to that. This approach is used with HeaderedContentControl derivatives (i.e. Expander) which essentially has 2 content properties, Content and Header. Using the new property would look like this:
<Border>
<local:UserControl>
<local:UserControl.OtherContent>
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
</local:UserControl.OtherContent>
</local:UserControl>
</Border>
And then inside the UserControl's XAML you need to explicitly set up the ContentPresenter Bindings (you only get them for free inside templates of ContentControls):
<DockPanel>
<!-- some content -->
<ContentPresenter Content="{Binding Path=OtherContent, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"/>
</DockPanel>
If you want a ContentTemplate, ContentTemplateSelector, or ContentStringFormat you'll also need to add properties and bindings for those.

Binding the button Content to the userControl Content

I have some userControl that contain simple button.
I want to bind the button Content to the userControl Content - How to do it?
Set a name for the user control (for example x:Name="self") and in the Button
<Button Content={Binding ElementName=self}" />
Do you mean this or something else?
If the Button is inside the UserControl it is part of the UserControl's Content and can't recursively contain itself. The whole purpose of a UserControl is that you're explicitly defining a fixed set of Content. If you want variable Content then you should use a templated ContentControl something like this:
<ContentControl Content="{Binding SomeVariableValue}">
<ContentControl.Template>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Border>
<!-- Other content from your user control -->
<Button Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>

How to change the style of a combobox to a label or hyperlink in WPF?

I want to change the style of a combo box control to look like a hyperlink.
When user clicks on the hyperlink ( combo box) it show options in the combobox to select.
The idea is that I want combo box control to display as a plain text ( more readable form).
If anybody created this type of sytle please let me know.
You could edit the ComboBox template and replace the ContentPresenter with a Hyperlink-style button. This should work pretty well, and it is just a bit of XAML coding. You can find the original ComboBox template here, or using Expression Blend.
EDIT:
OK, well, you have a ComboBox template which looks something like this (extremely simplified!):
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<!-- The popup that is displayed after you clicked on the ComboBox. -->
<Popup IsOpen="{TemplateBinding IsDropDownOpen}"
Placement="Bottom"/>
<!-- The button that is used to open the drop down. -->
<ToggleButton x:Name="btnOpenDropDown"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
<!-- The control which displays the currently selected item. -->
<ContentPresenter x:Name="contentPres"
Content="{TemplateBinding SelectionBoxItem}"/>
</Grid>
</ControlTemplate>
Actually, it is a bit more complicated because the ToggleButton must occupy the whole width (since the drop down should open whereever you click on the ComboBox), but it should display only on the right of the content. However, for your scenario, we can neglect this since you will not have a drop down button.
Now, since you only want to display the content as a hyperlink and no button besides it, you no longer need a distinction between ContentPresenter and ToggleButton. Therefore, instead of using a separate ContentPresenter, you could use the ToggleButton to present the content since it also has a Content property. Something like that:
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<!-- The popup that is displayed after you clicked on the ComboBox. -->
<Popup IsOpen="{TemplateBinding IsDropDownOpen}"
Placement="Bottom"/>
<!-- The button that is used to open the drop down AND to display the content (now). -->
<ToggleButton x:Name="btnOpenDropDown"
Content="{TemplateBinding SelectionBoxItem}"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
</Grid>
</ControlTemplate>
Of course, there are some additional properties to move from the ContentPresenter to the ToggleButton.
Now, all you have to do is define another template for the ToggleButton which looks like a Hyperlink (and then assign this template to the ToggleButton above). Actually, this should not be difficult assuming that your content is always a string (again, simplified!):
<Style x:Key="hyperlinkButtonStyle" TargetType="{x:Type ButtonBase}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<TextBlock Text="{TemplateBinding Content}"
TextDecorations="Underline"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This simplified code shows how you could do it. There are certainly other ways, and it still involves some work for you as the example was simplified. However, I cannot offer you the complete code.

Resources