I have a 'mvvm' based wpf application with a combobox style control Template like so,
<Style x:Key="Itmstyle" TargetType="ComboBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Button Content="{Binding Txt}" Command="{Binding DataContext.ClickCmd, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}}"
Background="Transparent" BorderBrush="Transparent">
<Button.InputBindings>
<KeyBinding Key="Enter" Command="{Binding DataContext.ClickCmd, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}}"/>
</Button.InputBindings>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
Style is used like this,
<ComboBox ItemContainerStyle="{StaticResource Itmstyle}"
ItemsSource="{Binding Values}"
DisplayMemberPath="Name"
SelectedIndex="{Binding Indx}"
IsSynchronizedWithCurrentItem="True"/>
When i use the keyboard arrow the buttons (in the style) do not get highlighted, only the dotted focus around the item appears; and when i press Enter key the ClickCmd is not executing.. Any idea! please let me know. Thanks a lot.
If you want a combo selection to both change selection AND fire a command, I'd say the easiest way would be to add a Button as you have done, but process the Click event in code behind to set the combo:
eg
<ComboBox.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Txt}"
Background="Transparent" BorderBrush="Transparent"
Command="{Binding DataContext.ClickCmd, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}}"
Click="ComboItem_Click"/>
</Button>
</DataTemplate>
</ComboBox.ItemTemplate>
Then in code behind:
private void ComboItem_Click(object sender, RoutedEventArgs e)
{
_combo.SelectedItem = ((Button)e.Source).DataContext;
}
Related
I am trying to add a context menu to an Xceed extended WPF datagrid. I am able to show the context menu and the commands fire from the menu, but right clicking on a row does not set it as the selected row and so the wrong record is used by the command.
Is there a way to change the way the selected item is set so that it can be updated by right click?
<xcdg:DataGridControl ItemsSource="{Binding Source={StaticResource RecordsForList}}" SelectedItem="{Binding SelectedRecord}">
<xcdg:DataGridControl.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding OpenCommand}" Header="Open" />
</ContextMenu>
</xcdg:DataGridControl.ContextMenu>
</xcdg:DataGridControl>
Instead of changing the way selecting an item works, you could pass the current item as a command parameter to the command if you set the ContextMenu property of each individual DataRow:
<xcdg:DataGridControl ItemsSource="{Binding Source={StaticResource RecordsForList}}" SelectedItem="{Binding SelectedRecord}">
<xcdg:DataGridControl.ItemContainerStyle>
<Style TargetType="xcdg:DataRow">
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=xcdg:DataGridControl}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="{Binding PlacementTarget.Tag.OpenCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding}" Header="Open" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</xcdg:DataGridControl.ItemContainerStyle>
</xcdg:DataGridControl>
The other option would be to write some code in the view that actually selects an item on right-click, e.g.:
<xcdg:DataGridControl ItemsSource="{Binding Source={StaticResource RecordsForList}}" SelectedItem="{Binding SelectedRecord}">
<xcdg:DataGridControl.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding OpenCommand}" Header="Open" />
</ContextMenu>
</xcdg:DataGridControl.ContextMenu>
<xcdg:DataGridControl.ItemContainerStyle>
<Style TargetType="xcdg:DataRow">
<EventSetter Event="PreviewMouseRightButtonDown" Handler="xgrid_PreviewMouseRightButtonDown" />
</Style>
</xcdg:DataGridControl.ItemContainerStyle>
</xcdg:DataGridControl>
private void xgrid_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
Xceed.Wpf.DataGrid.DataRow row = sender as Xceed.Wpf.DataGrid.DataRow;
xgrid.CurrentItem = row.DataContext;
}
Is there a quick and easy way to implement a color picker button like the one in MS Word?
I want the user to choose from a number of predefined colors in a dropdown. The selected color should be displayed when the button is closed. When the user clicks the button, an event or command should be triggered. I want to use one button instance to set the text color in a RichTextBox, and another one to set the background color (similar to MS Word).
My first approach was to use a ComboBox with a DataTemplate which contains a button.
This does not work: The button's command is not triggered when the user clicks it. Instead, the dropdown of the ComboBox opens. I don't know the exact reason, but maybe the IsHitTestVisible=false attributes in the ComboBox default template? See: http://msdn.microsoft.com/en-us/library/dd334408%28v=vs.95%29.aspx
<UserControl x:Class="MyBrushPicker"
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"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Height="Auto" Width="Auto" MinWidth="50">
<UserControl.Resources>
<!--
Provide two different DataTemplates:
- SelectionBoxTemplate is used to display the selected color when the ComboBox is closed.
It contains a button which should be clickable (does not work).
- NormalItemTemplate is used to display a color in the opened ComboBox popup.
See:
http://stackoverflow.com/questions/2214696/wpf-how-to-customize-selectionboxitem-in-combobox
-->
<DataTemplate x:Key="NormalItemTemplate">
<Border Height="44" Width="44" Margin="3,3,3,3" BorderThickness="1" BorderBrush="Black" Background="{Binding Mode=OneWay}"/>
</DataTemplate>
<DataTemplate x:Key="SelectionBoxTemplate">
<!-- This button cannot be clicked: PreviewMouseDown and Button_Click events are never triggered. -->
<Button Click="Button_Click" PreviewMouseDown="Button_PreviewMouseDown">
<Border Height="44" Width="44" Margin="3,3,3,3" BorderThickness="1" BorderBrush="Black" Background="{Binding Mode=OneWay}"/>
</Button>
</DataTemplate>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter"
Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}" />
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}"
Value="{x:Null}">
<Setter TargetName="Presenter" Property="ContentTemplate"
Value="{StaticResource SelectionBoxTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</UserControl.Resources>
<ComboBox IsReadOnly="True" IsEditable="False"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectableBrushes, Mode=OneWay}"
SelectedItem="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectedBrush, Mode=TwoWay}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
MinHeight="50"
MaxDropDownHeight="200"
ItemTemplate="{StaticResource CombinedTemplate}"
>
<ComboBox.Resources>
<Style TargetType="ComboBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" Width="200" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ComboBoxItem">
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="50" />
</Style>
</ComboBox.Resources>
</ComboBox>
</UserControl>
Is there a way to make the button work correctly?
Are there any better approaches?
EDIT: Using the Snoop tool revealed that the IsHitTestVisible attribute of the unnamed ContentPresenter inside the ComboBox is actually set to false (ValueSource: ParentTemplate). If I set this property to true using Snoop, the button becomes clickable.
Can I change this property from a style?
At least, the following doesn't work:
<ComboBox.Resources>
<Style TargetType="ContentPresenter">
<Setter Property="IsHitTestVisible" Value="True" />
</Style>
</ComboBox.Resources>
Assuming you are not just doing this as a learning exercise (which make using a library pointless)...
Take a look at: Extended WPF Toolkit - Color Picker
I've used it before for simple a plug-and-play WPF color-picker and it should solve your problem nicely.
I have a ListView that I want to be able to add a 'Delete' button to, with a command attached to it, when I select an item. I can't find anything about this on stack which is quite surprising.
One way you could do this is by applying a style to the ListViewItem in the ListView.Resources. In the Style, you set the ContentTemplate to a DataTemplate with only a TextBlock. Then you give the Style a Trigger that is tied to the IsSelected property. In the Trigger you'll set the ContentTemplate to a new DataTemplate with a TextBlock and a Button. The Command property of the Button is bound to the ViewModel using the RelativeSource binding and the CommandParameter is bound to the item so that it gets passed along as a parameter to your DeleteCommand.
XAML
<ListView Margin="3"
MinWidth="200"
ItemsSource="{Binding Items}">
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding SomeProperty}"
Margin="3" />
</DataTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding SomeProperty}"
Margin="3" />
<Button Content="Delete"
Margin="3"
Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"
CommandParameter="{Binding }" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListView.Resources>
</ListView>
ViewModel
private ICommand _DeleteCommand;
public ICommand DeleteCommand
{
get
{
if (_DeleteCommand == null)
{
_DeleteCommand = new RelayCommand<ExampleModel>(param => DeleteItem(param));
}
return _DeleteCommand;
}
}
private void DeleteItem(ExampleModel item)
{
MessageBox.Show(item.SomeProperty);
}
I just have the Delete method showing the value in a MessageBox for demo purposes. You should be able to modify the DataTemplate and Delete method to meet your needs.
I have the following xaml code defined for DataGridTextColumn of a DataGrid.
<DataGridTextColumn x:Name="UserIdColumn" Binding="{Binding Path=UserId}" HeaderStyle="{StaticResource DataGridHeaderStyle}"/>
And Here is the HeaderStyle for the DataGridTextColumn
<Style x:Key="DataGridHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<StackPanel>
<TextBlock x:Name="ColumnName" Text="UserId" />
<TextBox x:Name="UserIdFilter" Width="100">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<cal:ActionMessage MethodName="UserIdFilterChanged">
<cal:Parameter Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridTextColumn}}, Path=Name}"/>
<cal:Parameter Value="{Binding ElementName=UserIdFilter, Path=Text}" />
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
As one can see I am trying to access the DataGridTextColumn's Name property inside the DataGridColumnHeader's ControlTemplate using this line of code
<cal:Parameter Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridTextColumn}}, Path=Name}"/>
The above line is returning null, why ?
Is there any other way to get hold of DataGridTextColumn's Name property from DataGridColumnHeader's control template ?
You are recovering a null value for 2 different reasons:
The first one is that the DataGridTextColumn is not part of the visual tree and is not a visual instance of the datagrid. This is just an instance that descibes how a column of the datagrid should be displayed. Thus, DataGridTextColumn is not part of the ancestors of you cell.
The only way I see to recover your DataGridTextColumn is to proceed somehow like that:
<TextBox x:Name="UserIdFilter" Width="100" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}, Path=Columns[0]}">
This way, you should set the Tag property of the Textbox with the instance of the DataGridTextColumn (you can check with snoop that it works properly)
Then, the second reason explaining why you have a null value is that the DataGridTextColumn has no Name property.
What you could do is simply create an attached property like this one:
public class XamlHelper
{
public static readonly DependencyProperty NameProperty = DependencyProperty.RegisterAttached("Name", typeof(string), typeof(XamlHelper));
public static string GetName(DependencyObject obj)
{
return (string)obj.GetValue(NameProperty);
}
public static void SetName(DependencyObject obj, string value)
{
obj.SetValue(NameProperty, value);
}
}
... replace the name of the column definition by this one:
<DataGridTextColumn alias:XamlHelper.Name="UserIdColumn" Binding="{Binding Path=UserId}" HeaderStyle="{StaticResource DataGridHeaderStyle}"/>
and retrieve the property this way:
<Style x:Key="DataGridHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<StackPanel>
<TextBlock x:Name="ColumnName" Text="UserId" />
<TextBox x:Name="UserIdFilter" Width="100" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}, Path=Columns[0]}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<cal:ActionMessage MethodName="UserIdFilterChanged">
<cal:Parameter Value="{Binding ElementName=UserIdFilter, Path=Tag.(alias:XamlHelper.Name)}"/>
<cal:Parameter Value="{Binding ElementName=UserIdFilter, Path=Text}" />
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
... hope I didn't made too much mistakes in this code but I think something like that should work
Notice that I used the tag property of the textbox has a buffer because I am note sure the findAncestor mode of the binding is able to recover the datagrid properly (I had some issues on elements that are not is the visual tree). Maybe having a unique binding in you parameter also work...
Tag is also great to make tests because it allows to see the value in snoop!
Do not hesitate if some points are not clear enougth
edit:
I checked out the DataGridCOlumnHeaderClass and I discovered the property that seems to contain what you expected...
Thus, it should be possible to do that:
<Style x:Key="DataGridHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<StackPanel>
<TextBlock x:Name="ColumnName" Text="UserId" />
<TextBox x:Name="UserIdFilter" Width="100" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Column}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<cal:ActionMessage MethodName="UserIdFilterChanged">
<cal:Parameter Value="{Binding ElementName=UserIdFilter, Path=Tag.(alias:XamlHelper.Name)}"/>
<cal:Parameter Value="{Binding ElementName=UserIdFilter, Path=Text}" />
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
i have the following datatemplate in xaml and here i have textbox in datatemplate and button in normal XAML, what i really want is that i want to keep button be disabled until person enters something in textbox ? but its not working i have tried the follwing code
Please look into it and help me as well ! or is there any other way to do this let me know !
<UserControl.Resources>
<DataTemplate x:Key="dataTemplateParameter">
<StackPanel Width="170" Height="Auto" Margin="5">
<TextBlock Width="160" TextAlignment="Left" HorizontalAlignment="Left" Height="22" Text="{Binding Path=ParameterName, Mode=TwoWay}"/>
<TextBox x:Name="txtboxParameter" Width="160" Height="22" HorizontalAlignment="Left" Text="{Binding Path=ParameterValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</DataTemplate>
<Button Grid.Row="6" Name="SearchSourceParamBtn" Content="Search" VerticalAlignment="Bottom" Margin="12,5,92,0" Height="20" Visibility="{Binding SearchSourceBtnVisibility}" Command="{Binding SearchCommand}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text,x:ElementName=txtboxParameter}" Value="">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Thanks
nallskarthi
You can achieve this by
1) binding button to RelayCommand. Then,
2) Validate the "ParameterValue" in command's CanExecute delegate.
Edit:
The better way is to have the ParameterValue in ViewModel, but for some reason you won't do that. Then, create a static ChangeNotifyable property in VM, using this property do the validation in CanExecute delegate.
Pls Validate the "ParameterValue" in the CanExecute delegate of Command object.