I implemented a simple button with an image in it:
<Button Command="{Binding ButtonCommand, ElementName=ImageButtonControl}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ButtonImage, ElementName=ImageButtonControl}"/>
<TextBlock Text="{Binding ButtonText, ElementName=ImageButtonControl}" Margin="2,0,0,0"/>
</StackPanel>
</Button>
As you can see, I expose a ButtonCommand property in order to be able to attach an ICommand to this UserControl:
public partial class ImageButton : UserControl
{
/// <summary>
/// The dependency property that gets or sets the source of the image to render.
/// </summary>
public static DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ButtonImage", typeof(ImageSource), typeof(ImageButton));
public static DependencyProperty TextProperty =
DependencyProperty.Register("ButtonText", typeof(string), typeof(ImageButton));
public static DependencyProperty ButtonCommandProperty =
DependencyProperty.Register("ButtonCommand", typeof(ICommand), typeof(ImageButton));
public ImageButton()
{
this.DataContext = this;
InitializeComponent();
}
/// <summary>
/// Gets or sets the button command.
/// </summary>
public ICommand ButtonCommand
{
get { return (ICommand)GetValue(ImageButton.ButtonCommandProperty); }
set { SetValue(ImageButton.ButtonCommandProperty, value); }
}
/// <summary>
/// Gets or sets the button image.
/// </summary>
public ImageSource ButtonImage
{
get { return (ImageSource)GetValue(ImageButton.ImageSourceProperty); }
set { SetValue(ImageButton.ImageSourceProperty, value); }
}
/// <summary>
/// Gets or sets the button text.
/// </summary>
public string ButtonText
{
get { return (string)GetValue(ImageButton.TextProperty); }
set { SetValue(ImageButton.TextProperty, value); }
}
}
Then when I declare my button it gives this:
<uc:ImageButton Grid.Row="1" Grid.Column="0" ButtonCommand="{Binding AttachContextCommand}" ButtonImage="{StaticResource AssociateImage}" ButtonText="Associer"/>
And badaboom, nothing never happen when I click on my ImageButton.
When I replace the ImageButton with a simple button, the ICommand is called.
I even tried to simply extends the Button class and bind an ICommand, but once again, it didn't work...
Help appreciated !
Thx.
You can achieve this in a much cleaner way using a style and a couple of attached properties.
The attached properties will store your specific information.
The style will use these properties and build the look you want.
The element will still be a button so the command and everything else will work.
public class ImageButton
{
public static ImageSource GetImage(DependencyObject obj)
{
return (ImageSource)obj.GetValue(ImageProperty);
}
public static void SetImage(DependencyObject obj, ImageSource value)
{
obj.SetValue(ImageProperty, value);
}
public static readonly DependencyProperty ImageProperty =
DependencyProperty.RegisterAttached("Image", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata(null));
public static String GetCaption(DependencyObject obj)
{
return (String)obj.GetValue(CaptionProperty);
}
public static void SetCaption(DependencyObject obj, String value)
{
obj.SetValue(CaptionProperty, value);
}
public static readonly DependencyProperty CaptionProperty =
DependencyProperty.RegisterAttached("Caption", typeof(String), typeof(ImageButton), new UIPropertyMetadata(null));
}
<Style TargetType="{x:Type Button}"
x:Key="ImageButton">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=(local:ImageButton.Image), RelativeSource={RelativeSource AncestorType={x:Type Button}}}" />
<TextBlock Text="{Binding Path=(local:ImageButton.Caption), RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
Margin="2,0,0,0" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
You can then use this to declare buttons:
<Button Style="{DynamicResource ImageButton}"
local:ImageButton.Caption="Foo"
local:ImageButton.Image="..." />
Note:
I'm pretty sure it would be cleaner to go through the "Template" property and use a ControlTemplate and TemplateBindings, but that would mean re-creating the border and other stuff around your content, so if you are looking to just define a default "Content", my example would be the way to go, I think.
If the only added functionality that you want for your button is to have an image on it, then I think you're approaching this from the wrong direction. WPF is as awesome as it is because the UI controls are look-less. This means that a Control is merely a definition of functionality + some template to define how it looks. This means that the template can be swapped out at any time to change the look. Also, almost any content can be placed inside of almost any control
For instance, to define a button in your xaml that has the look your going for all you need is this:
<Window ...>
...
<Button Command="{Binding AttachContextCommand}">
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource AssociateImage}"/>
<TextBlock Text="Associer"/>
</StackPanel>
</Button>
...
</Window>
Just keep in mind that with WPF you don't have to define a new CustomControl or UserControl every time you want to change the look and feel of something. The only time you should need a CustomControl is if you want to add functionality to an existing Control or to create functionality that doesn't exist in any other Control.
Edit Due to comment:
If you're wanting to keep from defining the content for the button every time, the other option is to just have a poco (plain old CLR object) class that would define everything your interested in (I'll write my example as if you're doing this for a tool bar, because it makes sense to me):
public class ToolBarItem : INotifyPropertyChanged
{
public string Text { get ... set ... }
public ICommand Command { get ... set ... }
public ImageSource Image { get ... set ... }
}
That has a data template defined somewhere (App.xaml, Window.Resources, etc):
<DataTemplate DataType="{x:Type l:ToolBarItem}">
<Button Command="{Binding Command}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}"/>
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</Button>
</DataTemplate>
And then use the guy in your xaml like this:
<Window ...>
...
<ContentControl>
<ContentControl.Content>
<l:ToolBarItem Image="..." Command="..." Text="..."/>
</ContentControl.Content>
</ContentControl>
...
</Window>
I just don't know that the way you're trying to do it is the most WPF way you could do it.
EDIT Updated based on second comment
Sorry, I forgot to include the ContentControl surrounding that. Now that I remembered that, I realize that that's not much less verbose than the original where you are specifying the content manually. I'll post a new answer to help with your original question.
To re-answer the original question:
What I think you want to do is create a new CustomControl called ImageButton. Then change it to extend from Button instead of Control. You won't need a Command property since Button already has one. You'll only need to add an Image property and you can reuse the Content property from button instead of having a Text property.
When your CustomControl is created, it'll add an entry in your Generic.xaml for the default style of your ImageButton. In the Setter for the Template property you can change the ControlTemplate to this:
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<StackPanel Orientation="Horizontal">
<Image Source="{TemplateBinding Image}"/>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
Then, again, when you want to use it:
<Window ... >
...
<l:ImageButton Image="{StaticResource ...}" Command="...">
Associer
</l:ImageButton>
...
</Window>
The built-in WPF button contains code that fires the attached command in response to being clicked. Your "ImageButton" derives from UserControl, so you don't get that behavior. Probably the shortest route to get what you want is for your ImageButton class to actually derive from the WPF Button class. To accomplish that, change the markup for ImageButton from
<UserControl
...
>
...
</UserControl>
to
<Button
...
>
...
</Button>
Then change the base class of ImageButton from UserControl to Button.
You'll probably need to make some other minor changes before it all works.
Related
Perhaps I'm going about this the wrong way, but my layout is in a way where I have multiple Expanders in a TabControl and I want to add an "expand all" button.
Now logically this button should be inside the tab as it would control the elements in the tab so they ought to be grouped together. Visually however this would be a waste of space as I got a lot of empty space on the Tab Header bar (not sure what the terminology is, the row with the tabheaders).
So what I'm trying to achieve is adding a button outside the content of the tab. The canvas element seems to be what I need to use and it's working as far as its repositioning the element but it gets cut off. This is much easier to explain with a picture so
(if you look hard you can see where the button is as the header covering it is slightly translucent)
Now I can position it where I'd like it to be by moving it outside the TabItem but then I would have to write code to see which tab is focussed and hide it when it's not "Current" that is focussed. That to me sounds like the wrong way to do it as the only thing I want to do is move a button which is a 'view' type of thing.
My MainWindow.axaml:
<TabControl Grid.Row="1" Grid.Column="0" VerticalAlignment="Stretch">
<TabItem Header="Current" ZIndex="1">
<ScrollViewer Classes="CrawlersInAction">
<StackPanel>
<Canvas>
<Button Canvas.Right="10" Canvas.Top="-20" ZIndex="5">Expand All</Button>
</Canvas>
<!-- My very long template code for rendering the expanders -->
</StackPanel>
</ScrollViewer>
</TabItem>
</TabControl>
I do have a background in HTML/CSS so I thought Zindex would the trick and tried applying it in various places without any luck.
PS: I'm using Avalonia instead of WPF but it's pretty much a cross-platform clone, so any WPF know-how probably carries over 1:1.
If you think about it, this functionality lives in the ViewModel at the same "level" as the Tab Control.
<Grid>
<TabControl Items="{Binding MyTabViewModels}" SelectedItem={Binding SelectedTab} />
</Grid>
An Instance of MyTabViewModel has a collection on it:
public ObservableCollection<MyCollectionType> Items
The item class MyCollectionType has an IsExpanded property ...
public bool IsExpanded {get;set;}
Bound to your Expander control IsExpanded property.
Shove your button into the XAML
<Grid>
<TabControl Items="{Binding MyTabViewModels}" />
<Button Commmand={Binding ExpandAllCommand} />
</Grid>
Now on your base ViewModel your ICommand can do something like:
public void ExpandAllCommandExecuted()
{
foreach(var vm in SelectedTab.Items)
{
vm.IsExpanded = true;
}
}
Good luck, this is all pseudocode but illustrates a potential pattern.
The problem seems to have originated from placing my <canvas> control inside the <scrollviewer> control. I've placed it outside it whilst trying many things it seems and it works as I wanted it to. The buttons are visible rendering on top of the tabbar (TabStrip).
My XAML is now:
<TabControl Grid.Row="1" Grid.Column="0" VerticalAlignment="Stretch">
<TabItem Header="Current">
<StackPanel>
<Canvas>
<StackPanel Orientation="Horizontal" Canvas.Right="0" Canvas.Bottom="10" Spacing="5">
<Button Command="{Binding CollapseAll}" IsEnabled="{Binding !AllAreCollapsed}">Collapse All</Button>
<Button Command="{Binding ExpandAll}" IsEnabled="{Binding !AllAreExpanded}">Expand All</Button>
</StackPanel>
</Canvas>
<ScrollViewer Classes="CrawlersInAction">
<StackPanel>
<ItemsControl Name="itemscontrol" Items="{Binding SiteInfos}" VerticalAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander ExpandDirection="Down" IsExpanded="{Binding IsExpanded, Mode=TwoWay}" VerticalAlignment="Stretch">
<!-- Ommited my very long template code -->
</Expander>
<DataTemplate>
<ItemsControl.ItemTemplate>
<ItemsControl>
</StackPanel>
</ScrollViewer>
</StackPanel>
</TabItem>
</TabControl>
Codewise I ended up adding a "IsExpanded" property to my SiteInfo class that is used as the base for the expanders IsExpanded property and kept in sync by making it a two way binding as per the XAML above. The code on SiteInfo is:
public class SiteInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public static readonly bool StartIsExpanded = true;
private bool _isExpanded = StartIsExpanded;
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if (value != IsExpanded)
{
_isExpanded = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsExpanded)));
}
}
}
When I create my SiteInfo objects in MainWindowViewModel I subscribe to the events (siteInfo.PropertyChanged += SiteInfo_PropertyChanged;). When the event is received and it would change if my collapse or expand all button should be disabled it sends it own PropertyChangedEvent which then enables/disabled the control.
public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
{
public new event PropertyChangedEventHandler? PropertyChanged;
public ObservableCollection<SiteInfo> SiteInfos { get; }
= new ObservableCollection<SiteInfo>();
//Change SiteInfo.StartExpanded if you want this changed.
private bool _allAreExpanded = SiteInfo.StartIsExpanded;
public bool AllAreExpanded
{
get => _allAreExpanded;
set
{
if (_allAreExpanded != value)
{
_allAreExpanded = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(AllAreExpanded)));
}
}
}
//Change SiteInfo.StartExpanded if you want this changed.
private bool _allAreCollapsed = !SiteInfo.StartIsExpanded;
public bool AllAreCollapsed {
get { return _allAreCollapsed; }
set {
if (_allAreCollapsed != value)
{
_allAreCollapsed = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(AllAreCollapsed)));
}
}
}
private void SiteInfo_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == nameof(siteInfo.IsExpanded))
{
AllAreCollapsed = AreAllCollapsed();
AllAreExpanded = AreAllExpanded();
}
}
public bool AreAllCollapsed()
{
return !SiteInfos.Any<SiteInfo>( siteInfo => siteInfo.IsExpanded );
}
public bool AreAllExpanded()
{
return !SiteInfos.Any<SiteInfo>( siteInfo => siteInfo.IsCollapsed);
}
public void CollapseAll()
{
foreach(SiteInfo siteInfo in SiteInfos)
{
siteInfo.IsExpanded = false;
}
}
public void ExpandAll()
{
foreach (SiteInfo siteInfo in SiteInfos)
{
siteInfo.IsExpanded = true;
}
}
}
Figured I'd add the rest of my code in case anyone Googles this up and wants to do something similar.
So now when my program loads and everything is set to the default expanded true Expand All is disabled, Collapse all is enabled. Changing one expander to collapsed status will have both buttons enabled and collapsing all expanders will disable the Collapse All button.
I want to ask about the right way if I want to create Bindable user control consisting of two controls. I am not sure about what I am doing - whether I do it correctly , because I run into some problems.
Here is what I am trying to do:
Lets call this control ucFlagControl . Create new , custom user control ...
Its purpose is to show Color interpretation of logic ( True/ False ) value in variable , type of Bool.
What I used to do before was that I use Rectangle, and Bind FillProperty to boolean value using Converter
What I did to make it works was , that I made a usercontrol , and put rectangle and label inside
than I added this code:
public partial class ucStatusFlag : UserControl
{
public ucStatusFlag()
{
InitializeComponent();
}
public string LabelContent
{
get { return (string)GetValue(LabelContentProperty); }
set
{
SetValue(LabelContentProperty, value);
OnPropertyChanged("LabelContent");
}
}
///in case that I use integer or array
public int BitIndex
{
get { return (int)GetValue(BitIndexProperty); }
set
{
SetValue(BitIndexProperty, value);
OnPropertyChanged("BitIndex");
}
}
public string BindingSource
{
get { return (string)GetValue(BindingSourceProperty); }
set
{
SetValue(BindingSourceProperty, value);
OnPropertyChanged("BindingSource");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
/// <summary>
/// Identified the Label dependency property
/// </summary>
public static readonly DependencyProperty LabelContentProperty =
DependencyProperty.Register("LabelContent", typeof(string), typeof(ucStatusFlag), new PropertyMetadata("LabelContent"));
public static readonly DependencyProperty BitIndexProperty =
DependencyProperty.Register("BitIndex", typeof(int), typeof(ucStatusFlag), new PropertyMetadata(0));
public static readonly DependencyProperty BindingSourceProperty =
DependencyProperty.Register("(BindingSource", typeof(string), typeof(ucStatusFlag), new PropertyMetadata(""));
private void StatusFlag_Loaded(object sender, RoutedEventArgs e)
{
if (BindingSource.Length > 0)
{
Binding bind = new Binding();
string s = LabelContent;
int i = BitIndex;
bind.Converter = new StatusToColor();
bind.Path = new PropertyPath(BindingSource);
bind.ConverterParameter = BitIndex.ToString();
bind.Mode = BindingMode.OneWay;
bind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
recStatusBit.SetBinding(Rectangle.FillProperty, bind);
}
}
private class StatusToColor : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
byte bDataWordIdx;
byte bDataBitIdx;
Byte.TryParse((string)parameter, out bDataBitIdx);
if (Object.ReferenceEquals(typeof(UInt16[]), value.GetType()))
{
UInt16[] uiaData = (UInt16[])value;
bDataWordIdx = (byte)uiaData[0];
if ((uiaData[bDataBitIdx / 16] >> (bDataBitIdx % 16) & 0x1) == 1)
{
return Brushes.Green;
}
else
{
return Brushes.Red;
}
}
else if (Object.ReferenceEquals(typeof(UInt16), value.GetType()))
{
UInt16 uiaData = (UInt16)value;
if (((uiaData >> bDataBitIdx) & 0x1) == 1)
{
return Brushes.Green;
}
else
{
return Brushes.Red;
}
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return 0;
}
}
}
}
Than I realized that I can easily bind content and I do not have to create public static readonly DependencyProperty LabelContentProperty
but just property
public new string Content
{
get { return (string)label.Content; }
set
{
SetValue(label.Content, value);
OnPropertyChanged("Content");
}
}
this overrides the original content so I am able to Bind and/or assign the text of the label in upper level - in e.g. MainWindow.xaml where this user control is put
First question is if this is in this case OK or if there is some background I am not aware of and I should even such small controls do in different way - I would like to make dll. from it an load it to toolbox - I tested it works. And than use it in for example stack panel .
Second question is that I have problem with a rectangle "Fill" property . I am not able to bind that property like I bind content .
I know that the rectangle is derived from Shape class so I am not sure if it has something to do with this.
If I am able to do the inner binding or connection same as in
Content
I can remove the converters than and just bind it in e.g. MainWindow.xaml file (using the converter and converter parameter )
But FillProperty does not work for me so I am not sure about my point of view .
Thank you for suggestions
EDIT:
well I am sorry but I did not catch all you want to say in a comment below. Could you please explain closer ?
I know that the code above is not the right way to do it ... ?
Or can you post any article about it ?
my actual code is like this:
In a user control ... I removed all the code from code behind ...
' <Label x:Name="lStatusBit" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" Margin="2,1,17,2" />
<Rectangle x:Name="recStatusBit" Margin="0,3,1,7" />'
Content property works, I cant see Rectangle , and rectangle fill property ...
Other problem is if I fill in Content property in XAML where my uc is placed , Rectangle disappears .
I know I'm a year late to the party, but I'll answer incase anyone else comes across this.
My Suggestions
You should use a TextBlock control instead of Label controls if you want to display pure text. Labels have a content element which is re-rendered/computed many more times than a TextBlock's simple Text property.
You should avoid using magic strings, e.g. "LabelContent". You should use the C# nameof() expression when referencing property names. For example:
I use lambda expressions to clean up the code a bit, but this is just preference.
public string LabelContent
{
get => (string)GetValue(LabelContentProperty);
set => SetValue(LabelContentProperty, value);
}
public static readonly DependencyProperty LabelContentProperty =
DependencyProperty.Register(
nameof(LabelContent),
typeof(string),
typeof(ucStatusFlag),
new PropertyMetadata("Default Value"));
This will prevent runtime errors due to mistyped text, will allow you to jump to the property's reference, will make refactoring easier, and will make debugging easier by giving you a compile error that's easy to find (if the property doesn't exist).
I don't think you need the rectangle. If you're just trying to change the background color of the text area you can use a DataTrigger or make a converter.
DataTrigger Example
<TextBlock>
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<!-- The default value -->
<Setter Property="Background" Value="Transparent" />
<!-- Your trigger -->
<Style.Triggers>
<DataTrigger Binding="{Binding SomeBooleanValue}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
A DataTrigger is a quick and easy way to style a control by binding to a property on your ViewModel (assuming you're using the MVVM structure), but there are some cons - like reusing the same style on a different View whose ViewModel's properties are different. You'd have to rewrite the entire styling again.
Lets turn it into a reusable control where we can (1) specify a highlight background color, and (2) use a boolean to determine whether the control is highlighted.
Template Controls vs UserControls
I make my templated controls in a separate C# class file and put the control's styling in another separate resource dictionary file instead of using a UserControl.
These templated controls can consist of several other controls to make a single reusable control.
It's my understanding that UserControls are meant to use multiple templated controls (e.g. a TextBox) and link their interactions together to perform a specific way.
I don't think these controls are meant to be reusable in separate unrelated projects - they display data depending on your ViewModel which can be situational.
If you want to extend your custom control in the future via inheritance, then using a UserControl will make things difficult.
Here's what a few of my controls look like in the solution explorer:
Solution Files Snippet
The ExpansionPanel control in the snippet is an Expander with additional functionalities/properties.
The NavButton is a Button with additional functionalities/properties also.
I have a NavigationView UserControl that uses both of those controls to create something much larger than a templated control.
It sounds like you want to create a reusable templated control.
Creating a Custom Control
Here are the basic steps:
Create a "Themes" folder at the root of your project. It must be at the root of your project and spelling does matters.
Create a Generic.xaml Resource Dictionary file in the "Themes" folder. It must be directly under the "Themes" folder and spelling does matters.
This is where you store the default themes for your custom controls.
The template style for your control will automatically be added to the Generic.xaml file when you add a Custom Control template to your project.
<Style TargetType="{x:Type local:Example}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Example}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Personally, I like to have separate .xaml file for each control, and then I merge it into the Generic.xaml resource dictionary. This is just for organization purposes.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<!-- Control template styles -->
<ResourceDictionary Source="pack://application:,,,/Themes/ExpansionPanel.xaml" />
<ResourceDictionary Source="pack://application:,,,/Themes/NavButton.xaml" />
<ResourceDictionary Source="pack://application:,,,/Themes/TextDocument.xaml" />
<ResourceDictionary Source="pack://application:,,,/Themes/TextDocumentToolBar.xaml" />
<ResourceDictionary Source="pack://application:,,,/Themes/TextEditor.xaml" />
<ResourceDictionary Source="pack://application:,,,/Themes/HighlightTextBlock.xaml" />
<!-- etc... -->
</ResourceDictionary.MergedDictionaries>
<!-- Other styles or whatever -->
</ResourceDictionary>
It's important to note that order does matter if you have controls that depend on other controls.
Merge the Generic.xaml file into your App.xaml file.
<Application>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Other resource dictionaries... -->
<ResourceDictionary Source="pack://application:,,,/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- Other resource dictionaries... -->
</ResourceDictionary>
</Application.Resources>
</Application>
Why not just merge the control templates in the App.xaml file directly? WPF looks directly for the Generic.xaml file for custom type themes. App.xaml is also application specific and wouldn't be able to be usable in other applications if you used the library as a control library.
Create a .cs file using the built in Custom Control template OR a standard C# class file.
Your control's .cs file would resemble something similar to...
public class HighlightTextBlock : Control
{
#region Private Properties
// The default brush color to resort back to
public Brush DefaultBackground;
#endregion
static HighlightTextBlock()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HighlightTextBlock), new FrameworkPropertyMetadata(typeof(HighlightTextBlock)));
}
// Get the default background color and set it.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
DefaultBackground = Background;
}
#region Dependency Properties
/// <summary>
/// The text to display.
/// </summary>
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
nameof(Text), typeof(string), typeof(HighlightTextBlock), new PropertyMetadata(string.Empty));
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
/// <summary>
/// Whether or not the background should be highlighted.
/// </summary>
// This uses a callback to update the background color whenever the value changes
public static readonly DependencyProperty HighlightProperty = DependencyProperty.Register(
nameof(Highlight), typeof(bool),
typeof(HighlightTextBlock), new PropertyMetadata(false, HighlightPropertyChangedCallback));
public bool Highlight
{
get => (bool)GetValue(HighlightProperty);
set => SetValue(HighlightProperty, value);
}
/// <summary>
/// The highlight background color when <see cref="Highlight"/> is true.
/// </summary>
public static readonly DependencyProperty HighlightColorProperty = DependencyProperty.Register(
nameof(HighlightColor), typeof(Brush),
typeof(HighlightTextBlock), new PropertyMetadata(null));
public Brush HighlightColor
{
get => (Brush)GetValue(HighlightColorProperty);
set => SetValue(HighlightColorProperty, value);
}
#endregion
#region Callbacks
// This is the callback that will update the background
private static void HighlightPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var target = (HighlightTextBlock)dependencyObject;
if (target.Highlight)
target.Background = target.HighlightColor;
else
target.Background = target.DefaultBackground;
}
#endregion
}
Create a ResourceDictionary.xaml file to store your control's template and style OR add it directly in Generic.xaml.
Your .xaml file would look something like...
<Style x:Key="HighlightTextBlock" TargetType="{x:Type ctrl:HighlightTextBlock}">
<!-- Default setters... -->
<!-- Define your control's design template -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ctrl:HighlightTextBlock}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<!--
I only bound the Text and Background property in this example
Make sure to bind other properties too.. like Visibility, IsEnabled, etc..
-->
<TextBlock Text="{TemplateBinding Text}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--
Set the default style for the control
The above style has a key, so controls won't use that style
unless the style is explicitly set.
e.g.
<ctrl:HighlightTextBlock Style={StaticResource HighlightTextBlock} />
The reason I used a key above is to allow extending/reusing that default style.
If a key wasn't present then you wouldn't be able to reference it in
another style.
-->
<Style TargetType="{x:Type ctrl:HighlightTextBlock}" BasedOn="{StaticResource HighlightTextBlock}" />
Add a reference to the control's resource dictionary in Generic.xaml, like in step 2's code snippet.
Usage:
I'm binding the IsChecked property to a IsHighlighted property on my ViewModel.
You can bind it to whatever.
<StackPanel>
<ToggleButton IsChecked="{Binding IsHighlighted}" Content="{Binding IsHighlighted}"
Width="100" Height="35" Margin="5"/>
<ctrl:HighlightTextBlock Background="Transparent" HighlightColor="Red"
Text="HELLO WORLD!!!" Highlight="{Binding IsHighlighted}"
Width="100" Height="35" HorizontalAlignment="Center" />
</StackPanel>
On False Snippet
On True Snippet
Your controls may look a bit different - I'm using a custom dark theme.
Finally I found this working :
XAML
<UserControl x:Class="ucStatusFlag"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="17" d:DesignWidth="100"
x:Name="StatusFlag">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle x:Name="recStatusBit" Grid.Column="0" Stroke="Black" Width="11" Fill="{Binding ElementName=StatusFlag, Path=RectangleColor}" Margin="0,2,0.2,3.8" />
<Label Height="17" x:Name="lStatusBit" Foreground="Black" Grid.Column="1" Padding="0" VerticalContentAlignment="Center" Margin="5,0,0,1" Content="{Binding ElementName=StatusFlag, Path=LabelContent}" />
</Grid>
C#
public partial class ucStatusFlag : UserControl
{
public ucStatusFlag()
{
InitializeComponent();
}
public string LabelContent
{
get { return (string)GetValue(LabelContentProperty); }
set
{
SetValue(LabelContentProperty, value);
}
}
public SolidColorBrush RectangleColor
{
get { return (SolidColorBrush)GetValue(RectangleColorProperty); }
set
{
SetValue(RectangleColorProperty, value);
}
}
public static readonly DependencyProperty RectangleColorProperty =
DependencyProperty.Register("RectangleColor", typeof(SolidColorBrush), typeof(ucStatusFlag), new PropertyMetadata(Brushes.Gold));
public static readonly DependencyProperty LabelContentProperty =
DependencyProperty.Register("LabelContent", typeof(string), typeof(ucStatusFlag), new PropertyMetadata("LabelContent"));
}
Binding in Another Project :
<ucStatusFlag HorizontalAlignment="Left" Height="18" Margin="154,224,0,0" VerticalAlignment="Top" Width="100" LabelContent="ABC" RectangleColor="{Binding RectangleColorPropertyInProject}"/>
Where RectangleColorPropertyInProject is Property In certain project view model
This question is similar to Windows Phone 8.1 Toggling the visibility of a TextBlock in a DataTemplate
and countless others, but none of these ideas are working. The Loaded event is never triggered after I add my Textblock to my datatemplate in my hub. The Visual Tree search is not finding my TextBlock.
I have tried a basic binding like this:
<HubSection Background="{StaticResource HubSectionBackgroundBrush}"
MaxWidth="{x:Bind DesiredHubSectionWidth, Mode=OneWay}"
Header="You have selected:" Padding="60"
>
<DataTemplate x:DataType="local:Scenario4">
<TextBlock TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Left" Text="{Binding Item}"/>
</DataTemplate>
</HubSection>
with
public string Item { get; set; }
Item = makeText.Text;
But this doesn't work (Text on the Hub is always empty). From looking at previous posts and code I have come up with this xaml code using Dependency Properties:
<HubSection Background="{StaticResource HubSectionBackgroundBrush}"
MaxWidth="{x:Bind DesiredHubSectionWidth, Mode=OneWay}"
Header="You have selected:" Padding="60"
>
<DataTemplate x:DataType="local:Scenario4">
<TextBlock TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Left" Text="{x:Bind DesiredSelectionText, Mode=OneWay}"/>
</DataTemplate>
</HubSection>
with this in the c#
private static DependencyProperty s_desiredHubSectionWidthProperty
= DependencyProperty.Register("DesiredHubSectionWidth", typeof(double), typeof(Scenario4), new PropertyMetadata(560.0));
private static DependencyProperty selectionText = DependencyProperty.Register("SelectionText", typeof(string), typeof(Scenario4), new PropertyMetadata("Nothing"));
public static DependencyProperty DesiredHubSectionWidthProperty
{
get { return s_desiredHubSectionWidthProperty; }
}
public static DependencyProperty DesiredSelectionTextProperty
{
get { return selectionText; }
}
public string DesiredSelectionText
{
get { return (string)GetValue(selectionText); }
set { SetValue(selectionText, value); }
}
public double DesiredHubSectionWidth
{
get { return (double)GetValue(s_desiredHubSectionWidthProperty); }
set { SetValue(s_desiredHubSectionWidthProperty, value); }
}
and I set the text with
DesiredSelectionText = makeText.Text;
The width binding works perfectly, but the text is not updating. What is the proper way to change Hub/DataTemplate Text at Runtime? Since the Hub is not even printing "Nothing", something must be really wrong.
As a last resort I am thinking I will just construct my own datatemplate and assign it at runtime, but the only code I can find for that is deprecated(uses FrameworkElementFactory).
I was trying to assign to the textblock in the OnNavigatedTo method which is called before the textblock's Loaded method. That's why my program was showing the textblock as null.
Turns out the link I posted is the solution, just for me, the textblock was loaded after the page was navigated to.
Before any item in a ComboBox is selected, its SelectedItemis null and the ComboBox itself is visually blank. Once something is selected, there doesn't seem to be any way for the user to select "the absence of a selection" (though it can be done by setting SelectedItem to null in code).
My ComboBoxes are bound to ObservableCollections of my objects. I don't want to add a "special" first null-like object to the front of every ObservableCollection. So I'm taking this opportunity to learn a bit about writing a UserControl.
The problem is SelectedItem doesn't work the way it normally does. That is, the ComboBox is nicely bound to a backing ObservableCollection, but picking something from the ComboBox doesn't update the SelectedItem it's supposed to be bound to.
I feel like I need to be passing along some information from the ComboBox in the UserControl to...somewhere. Am I on the right track? What should I be googling for?
C#:
public partial class ClearableComboBox : UserControl
{
public ClearableComboBox()
{
InitializeComponent();
}
public IEnumerable ItemsSource
{
get { return (IEnumerable)base.GetValue(ItemsSourceProperty); }
set { base.SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource",
typeof(IEnumerable),
typeof(ClearableComboBox));
public object SelectedItem
{
get { return (object)base.GetValue(SelectedItemProperty); }
set { base.SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem",
typeof(object),
typeof(ClearableComboBox));
public string DisplayMemberPath
{
get { return (string)base.GetValue(DisplayMemberPathProperty); }
set { base.SetValue(DisplayMemberPathProperty, value); }
}
public static readonly DependencyProperty DisplayMemberPathProperty =
DependencyProperty.Register("DisplayMemberPath",
typeof(string),
typeof(ClearableComboBox));
private void Button_Click(object sender, RoutedEventArgs e)
{
comboBox.SelectedItem = null;
}
}
XAML:
<UserControl x:Class="MyProj.ClearableComboBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="root">
<DockPanel>
<Button DockPanel.Dock="Left" Click="Button_Click" ToolTip="Clear">
<Image Source="pack://application:,,,/img/icons/silk/cross.png" Stretch="None" />
</Button>
<ComboBox
Name="comboBox"
ItemsSource="{Binding ElementName=root, Path=ItemsSource}"
SelectedItem="{Binding ElementName=root, Path=SelectedItem}"
DisplayMemberPath="{Binding ElementName=root, Path=DisplayMemberPath}" />
</DockPanel>
</UserControl>
Usage:
<wpfControl:ClearableComboBox ItemsSource="{Binding Path=Things}"
DisplayMemberPath="SomeProperty"
SelectedItem="{Binding Path=SelectedThing}" />
// Picking a Thing doesn't update SelectedThing :(
Since combobox derives from Selector class which in turn derives from ItemsControl. So, by deriving from UserControl you are devoiding your combobox with properties of Selector class which might internally handle the Selection thing for you. so, i would suggest instead of deriving it from UserControl, you should derive it from Combobox like this -
public partial class ClearableComboBox : ComboBox
So, that ways you won't have to override the ItemsSource, DisplayMemberPath etc. in your class since it s already present in the ComboBox class. You can always extend your class further to provide addidtional features which is in your case setting the SelectedItem to null on some button click. Hope this is what you want..
EDIT (Custom Control)
Creating a Custom Control is your answer here, to get started if you are not aware of it, look at this for start - http://www.wpftutorial.net/HowToCreateACustomControl.html
When you create a Custom Control say CustomControl1, replace the template for CustomControl1 in your Generic.xaml file with this one -
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<Button Name="btn" DockPanel.Dock="Left" ToolTip="Clear" Width="20">
<Image Source="pack://application:,,,/img/icons/silk/cross.png" Stretch="None" />
</Button>
<ComboBox Name="comboBox"
ItemsSource="{TemplateBinding ItemsSource}"
SelectedItem="{TemplateBinding SelectedItem}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}" />
</DockPanel>
</Border>
</ControlTemplate>
By default your CustomControl1 class will be derived from Control. Replace it to derive from class ComboBox so that you don't have declare DP's yet over again like this and copy paste this code there -
public class CustomControl1 : ComboBox
{
private Button clearButton;
private ComboBox comboBox;
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
clearButton = GetTemplateChild("btn") as Button;
comboBox = GetTemplateChild("comboBox") as ComboBox;
clearButton.Click += new RoutedEventHandler(clearButton_Click);
}
private void clearButton_Click(object sender, RoutedEventArgs e)
{
comboBox.SelectedItem = null;
}
}
Now, your CustomControl1 class is ready for use in your other xaml files like this -
<local:CustomControl1 ItemsSource="{Binding YourSource}"
SelectedItem="{Binding YourSelectedItem}"
Height="50" Width="200"/>
I chose to handle the key press event on the combo box and handle the escape key press to clear out the combo box's SelectedItem.
I think there's a better way, develop a wrapper/Adorner for ComboBox, that adds a button next to the ComboBox and wipe the selection on click.
I have just started learning MVVM. I've made the application from scratch by following this MVVM tutorial (I highly recommend it to all MVVM beginners out there). Basically, what I have created so far is a couple of text boxes where user adds his or her data, a button to save that data which subsequently populates the ListBox with all entries made.
Here's where I got stuck: I want to be able to double-click on a ListBoxItem and to trigger a command that I have created and added to my ViewModel. I don't know how to finish the XAML side, i.e. I don't know how to bind that command to the ListBox(Item).
Here's XAML:
...
<ListBox
Name="EntriesListBox"
Width="228"
Height="208"
Margin="138,12,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemsSource="{Binding Entries}" />
...
Here's ViewModel:
public class MainWindowViewModel : DependencyObject
{
...
public IEntriesProvider Entries
{
get { return entries; }
}
private IEntriesProvider entries;
public OpenEntryCommand OpenEntryCmd { get; set; }
public MainWindowViewModel(IEntriesProvider source)
{
this.entries = source;
...
this.OpenEntryCmd = new OpenEntryCommand(this);
}
...
}
And finally, here's the OpenEntryCommand that I want to be executed once the user double-clicks the item in the EntriesListBox:
public class OpenEntryCommand : ICommand
{
private MainWindowViewModel viewModel;
public OpenEntryCommand(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return parameter is Entry;
}
public void Execute(object parameter)
{
string messageFormat = "Subject: {0}\nStart: {1}\nEnd: {2}";
Entry entry = parameter as Entry;
string message = string.Format(messageFormat,
entry.Subject,
entry.StartDate.ToShortDateString(),
entry.EndDate.ToShortDateString());
MessageBox.Show(message, "Appointment");
}
}
Please help, I'd appreciate it.
Unfortunately, only ButtonBase derived controls have the possibility for binding ICommand objects to their Command properties (for the Click event).
However, you can use an API provided by Blend to map an event (like in your case MouseDoubleClick on the ListBox) to an ICommand object.
<ListBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding YourCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
You'll have to define: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" and have a reference to System.Windows.Interactivity.dll.
-- EDIT --
This is part of WPF4, but u can use Microsoft.Windows.Interactivity if you're not using WPF4. This dll is from Blend SDK, which doesn't require Blend, from here:
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=f1ae9a30-4928-411d-970b-e682ab179e17&displaylang=en
Update: I found something that should help you. check this link on MVVM Light Toolkit which contains a walkthrough on how to do this, along with a link to the needed libraries. MVVM Light Toolkit is a very interesting framework for applying MVVM with Silverlight, WPF, and WP7.
Hope this helps :)
This is made tricky because of the DoubleClick event. There are a few ways to do this:
Handle the double-click event in code behind, and then manually invoke a command/method on your ViewModel
Use an attached behavior to route the DoubleClick event to your Command
Use a Blend Behavior to map the DoubleClick event to your command
2 and 3 might be more pure, but frankly, 1 is easier, less complex, and not the worst thing in the world. For a one-off case, I'd probably use approach #1.
Now, if you changed your requirements to use, say, a hyperlink on each item, it would be easier. Start out by naming the root element in your XAML - e.g., for a Window:
<Window .... Name="This">
Now, in the DataTemplate for your ListBox items, use something like this:
<ListBox ...>
<ListBox.ItemTemplate>
<DataTemplate>
<Hyperlink
Command="{Binding ElementName=This, Path=DataContext.OpenEntryCmd}"
Text="{Binding Path=Name}"
/>
The ElementName binding lets you resolve the OpenEntryCmd from the context of your ViewModel, rather than the specific data item.
EDIT: I wrote this post as an inexperienced WPF developer, nowadays I'd either use a framework that provides event to command binding, or simply use a button and restyle it. Of course for maximum flexibility this is maybe better.
I find the best way to do this is to create a simple user control wrapper for my content, with dependency properties for the command and parameter.
The reason I did this was due to the Button not bubbling the click event to my ListBox which prevented it from selecting the ListBoxItem.
CommandControl.xaml.cs:
public partial class CommandControl : UserControl
{
public CommandControl()
{
MouseLeftButtonDown += OnMouseLeftButtonDown;
InitializeComponent();
}
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
if (Command != null)
{
if (Command.CanExecute(CommandParameter))
{
Command.Execute(CommandParameter);
}
}
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand),
typeof(CommandControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object),
typeof(CommandControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
}
CommandControl.xaml:
<UserControl x:Class="WpfApp.UserControls.CommandControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Background="Transparent">
</UserControl>
Usage:
<ListBoxItem>
<uc:CommandControl Command="{Binding LoadPageCommand}"
CommandParameter="{Binding HomePageViewModel}">
<TextBlock Text="Home" Margin="0,0,0,5" VerticalAlignment="Center"
Foreground="White" FontSize="24" />
</uc:CommandControl>
</ListBoxItem>
The Content can be whatever, and when the control is clicked, it will execute the command.
EDIT: Added Background="Transparent" to UserControl to enable click events on the entire area of the control.
This is a bit of a hack, but it works well and allows you to use commands and avoid code behind. This also has the added benefit of not triggering the command when you double-click (or whatever your trigger is) in the empty ScrollView area assuming your ListBoxItems don't fill the entire container.
Basically, just create a DataTemplate for your ListBox that is composed of a TextBlock and bind the width of the TextBlock to the width of the ListBox, set the margins and padding to 0, and disable horizontal scrolling (because the TextBlock will bleed beyond the visible bounds of the ScrollView triggering the horizontal scroll bar otherwise). The only bug I've found is that the command won't fire if the user clicks precisely on the border of the ListBoxItem, which I can live with.
Here is an example:
<ListBox
x:Name="listBox"
Width="400"
Height="150"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ItemsSource="{Binding ItemsSourceProperty}"
SelectedItem="{Binding SelectedItemProperty}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Padding="0"
Margin="0"
Text="{Binding DisplayTextProperty}"
Width="{Binding ElementName=listBox, Path=Width}">
<TextBlock.InputBindings>
<MouseBinding
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.SelectProjectCommand}"
Gesture="LeftDoubleClick" />
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I recently needed to trigger an ICommand upon double clicking a ListBoxItem as well.
Personally, I don't like the DataTemplate method as it is binding to the content inside the ListBoxItem container, and not the container itself. I've opted to use an Attached Property to assign an InputBinding on the container. It takes a little more elbow grease, but it works well.
First, we need to create an attached property class. I've created mine a little more generically towards any class that derives from FrameworkElement, just in case I run into this again with a different visual.
public class FrameworkElementAttachedProperties : DependencyObject
{
public static readonly DependencyProperty DoubleClickProperty = DependencyProperty.RegisterAttached("DoubleClick", typeof(InputBinding),
typeof(FrameworkElementAttachedProperties), new PropertyMetadata(null, OnDoubleClickChanged));
public static void SetDoubleClick(FrameworkElement element, InputBinding value)
{
element.SetValue(DoubleClickProperty, value);
}
public static InputBinding GetDoubleClick(FrameworkElement element)
{
return (InputBinding)element.GetValue(DoubleClickProperty);
}
private static void OnDoubleClickChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = obj as FrameworkElement;
/// Potentially throw an exception if an object is not a FrameworkElement (is null).
if(e.NewValue != null)
{
element.InputBindings.Add(e.NewValue as InputBinding);
}
if(e.OldValue != null)
{
element.InputBindings.Remove(e.OldValue as InputBinding);
}
}
}
Then the final step is to override the base container style for the ListBoxItem.
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}"
BasedOn="{StaticResource ListBoxItem}">
<Setter Property="local:FrameworkElementAttachedProperties.DoubleClick">
<Setter.Value>
<MouseBinding Command="{Binding OnListBoxItemDoubleClickCommand}"
MouseAction="LeftDoubleClick"/>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
Now, anytime a ListBoxItem is double clicked, it will fire our OnListBoxItemDoubleClickCommand.
If you're looking for a nice simple solution that uses interactions instead of mucking about with user controls, code behind, input bindings, custom attached properties, etc.
And you want something that works at the ListBoxItem level, i.e. not ListBox level as per the (incorrectly) accepted solution.
Then here's a snippet for a simple 'button like' click action..
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<!-- insert your visuals here -->
<b:Interaction.Triggers>
<b:EventTrigger EventName="MouseUp">
<b:InvokeCommandAction Command="{Binding YourCommand}" />
</b:EventTrigger>
</b:Interaction.Triggers>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note, background="Transparent" is required to ensure the entire Grid is clickable and not just the contents inside.