I need to use a context menu to restart services based on a datagrid (the context menu should send the name of the service as a CommandParameter)
The ContextMenu shows properly but clicking the MenuItems does not trigger the command (breakpoint not reached) but setting the context menu directly to a checkbox for example triggers the commands perfectly.
Ultimately, what I want is to get the context menu on right click on any item of the datagrid, clicking the menuitem should send either the Name column value or the whole selected item to the command in ViewModel (and then restart the process from there).
Really hurting my head for a few days, if you have any hints...
My Datagrid :
<DataGrid x:Name="dgServices" HorizontalAlignment="Left" VerticalAlignment="Top" DockPanel.Dock="Top" Height="auto" HeadersVisibility="Row"
AutoGenerateColumns="False" HorizontalGridLinesBrush="#FFC7E0EE" VerticalGridLinesBrush="#FFC7E0EE" SelectionMode="Single" BorderThickness="0"
ItemContainerStyle="{StaticResource DefaultRowStyle}" ItemsSource="{Binding Source={StaticResource cvsServ}}">
<DataGrid.GroupStyle>
//GROUPSTYLE
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="30"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="373">
</DataGridTextColumn>
<DataGridTextColumn Header="Status" Binding="{Binding Status}" Width="356"/>
</DataGrid.Columns>
</DataGrid>
ContainerStyle and ContextMenu in Window.Resources :
<Window.Resources>
<ContextMenu x:Key="RowMenu" DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="NULL" Command="{Binding restartService}"/>
<MenuItem Header="NAME" CommandParameter="{Binding PlacementTarget.Name, RelativeSource={RelativeSource FindAncestor,AncestorType=ContextMenu}}" Command="{Binding restartService}"/>
<MenuItem Header="ITEM" CommandParameter="{Binding PlacementTarget.SelectedItem, RelativeSource={RelativeSource FindAncestor,AncestorType=ContextMenu}}" Command="{Binding restartService}"/>
<MenuItem Header="ITEMS" CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource FindAncestor,AncestorType=ContextMenu}}" Command="{Binding restartService}"/>
</ContextMenu>
<Style x:Key="DefaultRowStyle" TargetType="{x:Type DataGridRow}">
<Setter Property="ContextMenu" Value="{StaticResource RowMenu}" />
</Style>
<CollectionViewSource x:Key="cvsServ" Source="{Binding services, UpdateSourceTrigger=PropertyChanged}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Key" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
The Checkbox used for testing :
<CheckBox IsChecked="{Binding autoStart, Mode=TwoWay}">
<Label Content="AutoStart"/>
<CheckBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit" CommandParameter="{Binding PlacementTarget, RelativeSource={RelativeSource FindAncestor,AncestorType=ContextMenu}}" Command="{Binding restartService}"/>
<!--<MenuItem Header="Edit" CommandParameter="{Binding ElementName=dgServices, Path=SelectedItem}" Command="{Binding restartService}"/>-->
</ContextMenu>
</CheckBox.ContextMenu>
</CheckBox>
ViewModel :
private DelegateCommand _restartService;
public DelegateCommand restartService
{
get
{
return _restartService ?? (_restartService = new DelegateCommand(o => TestCommand(o), o => true));
//return _restartService ?? (_restartService = new DelegateCommand(o => MessageBox.Show($"{o.ToString()}\n{o.GetType()}"), o => true));
//return _restartService ?? (_restartService = new DelegateCommand(o => Process.Start(new ProcessStartInfo("C:\\Windows\\system32\\cmd.exe", "/c net start "+o.ToString()) { Verb = "runas" }), o => true));
}
private void TestCommand(object o)
{
MessageBox.Show(o?.GetType()?.ToString() ?? "NULL");
}
If the command property is defined in the view model of the DataGrid, you could bind the Tag property of the row to the DataGrid:
<Style x:Key="DefaultRowStyle" TargetType="{x:Type DataGridRow}">
<Setter Property="ContextMenu" Value="{StaticResource RowMenu}" />
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}" />
</Style>
...and then try this:
<MenuItem Header="NAME"
Command="{Binding PlacementTarget.Tag.DataContext.restartService, RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}}"
CommandParameter="{Binding PlacementTarget.Tag.Name, RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}}"/>
Related
I am trying to bind DataGrid column header to its own ContextMenu like this:
<DataGrid x:Name="AllLogs">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Show/Hide Columns"
ItemsSource="{Binding ElementName=AllLogs, Path=Columns}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"></TextBlock>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
Its always sends the following error in output:
Cannot find source for binding with reference
'ElementName=AllLogs'. BindingExpression:Path=Columns;
DataItem=null; target element is 'MenuItem' (Name=''); target property
is 'ItemsSource' (type 'IEnumerable')
EDIT: Binding with a ComboBox works as expected
<ComboBox ItemsSource="{Binding ElementName=AllLogs, Path=Columns}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Header}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You should set first the DataContext of ContextMenu so that ItemsSource bind to Menu Item can inherit the same DataContext.
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show/Hide Columns"
ItemsSource="{Binding Columns}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"></TextBlock>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
The reason that ContextMenu didn't work but ComboBox did is that ContextMenu is a Popup, that means it's not part of DataGrid's visual tree, so ElementName would not work as ComboBox did. In fact, #user1672994 was vary close to the answer.
<DataGrid x:Name="AllLogs">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Show/Hide Columns"
ItemsSource="{Binding PlacementTarget.Columns, RelativeSource={RelativeSource AncestorType=ContextMenu}}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
The problem/misunderstanding is that ContextMenu isn't a part of a visual tree.
So ContextMenu.PlacementTarget is your "connection" to the UIElement, which is in the visual tree, therefore you have to go via PlacementTarget to access the elements from visual tree.
MSDN about PlacementTarget:
When the ContextMenu is assigned to the FrameworkElement.ContextMenu
or FrameworkContentElement.ContextMenu property, the
ContextMenuService changes this value of this property to the owning
FrameworkElement or FrameworkContentElement when the ContextMenu opens
In answer below you don't have to traverse searching an ancestor type, but do use an UIElement as DataContext for ContextMenu:
<DataGrid.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show/Hide Columns" ItemsSource="{Binding Columns}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
I don't know the RadGrivView control per say, but the error means that it cannot find an IEnumerable property called "Columns" on your element. Are you sure it is a publicly accessible collection for the control?
ElementName uses VisualTree to find out desired element, it its not part of the current visual tree - up or down as it is with context menu you will get exception.
You could use Binding Source={x:Reference AllLogs} that does not use VisualTree, but unfortunately in your use case you would get exception of circular reference if you use it directly without style.
What you need to use is RelativeSource binding.
<Window.Resources>
<ContextMenu x:Key="headerMenu">
<ContextMenu.Items>
<MenuItem
Header="Show/Hide Columns"
ItemsSource="{Binding Columns, RelativeSource={RelativeSource AncestorType=DataGrid}}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu.Items>
</ContextMenu>
<Style TargetType="{x:Type DataGrid}">
<Setter Property="ContextMenu" Value="{StaticResource headerMenu}" />
</Style>
</Window.Resources>
<Grid>
<DataGrid x:Name="AllLogs">
<DataGrid.Columns>
<DataGridTextColumn Header="ID"></DataGridTextColumn>
<DataGridTextColumn Header="Name"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
This would work as well - with Reference:
<Window.Resources>
<ContextMenu x:Key="headerMenu">
<ContextMenu.Items>
<MenuItem
Header="Show/Hide Columns"
ItemsSource="{Binding Source={x:Reference AllLogs}, Path=Columns}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu.Items>
</ContextMenu>
<Style TargetType="{x:Type DataGrid}">
<Setter Property="ContextMenu" Value="{StaticResource headerMenu}" />
</Style>
</Window.Resources>
<Grid>
<DataGrid x:Name="AllLogs">
<DataGrid.Columns>
<DataGridTextColumn Header="ID"></DataGridTextColumn>
<DataGridTextColumn Header="Name"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
I have an ObservableCollection of objects. These object are displayed in a DataGrid & a `SelectedObject
I have a property PossibleParentObjects that returns a List of Objects based on the SelectedObject.
I would like to have this property populate a ComboBox residing in a column of the DataGrid
How can I do this?
Here is what I have so far... Obviously not working:
<DataGrid Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
AlternatingRowBackground="AntiqueWhite" AlternationCount="2"
ItemsSource="{Binding AllObjects, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedObject}"
CanUserAddRows="True" CanUserDeleteRows="True"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name, Mode=TwoWay}" Header="Name"/>
<DataGridTextColumn Binding="{Binding Abbr, Mode=TwoWay}" Header="Abbreviation"/>
<DataGridComboBoxColumn Header="Parent Object" Width="120"
SelectedItemBinding="{Binding Path=Id, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name"
ItemsSource="{Binding Path=AllObjects,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
</DataGridComboBoxColumn>
<DataGridTextColumn Binding="{Binding Desc, Mode=TwoWay}" Header="Description"/>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="CurrentCellChanged">
<i:InvokeCommandAction Command="{Binding DataGridChanged}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
see this Stackoverflow question which discusses a similar problem.
The basic idea is the following:
bind the datagrid column to a single property in your viewmodel, let's call it "ParentObjects"
bind the selected row of the datagrid to another property in the viewmodel.
In the setter of that property you should retrieve the data-items you need for the combo box of the DataGridComboBox column, and use this to set the "ParentObjects" property
This way, whenever the user changes the row he wants to see, it will automatically retrieve the correct objects and populate the combobox column.
In other words, you don't retrieve it using the source of the combo box but you retrieve upon change of the selected row. You'll have to do this - the property system does not allow parameters.
I know it is a general description I give here, not code, but I think you'll get the gist.
This is the final code that ended up working for me. It is the "Answer", but it was Nepdev's answer that got me here. Hopefully this will help others out trying to do something similar.
<DataGrid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HeadersVisibility="Column"
AlternatingRowBackground="AntiqueWhite" AlternationCount="2"
ItemsSource="{Binding EquipLocations, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedItem}"
CanUserAddRows="True" CanUserDeleteRows="True"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name, Mode=TwoWay}" Header="Name"/>
<DataGridTextColumn Binding="{Binding Abbr, Mode=TwoWay}" Header="Abbreviation"/>
<DataGridComboBoxColumn Header="Uses Location" Width="120"
SelectedValueBinding="{Binding Path=ParentObjectId, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedValuePath="Id"
DisplayMemberPath="Abbr">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path=DataContext.AllPossibleObjects}"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path=DataContext.PossibleParentObjects}"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DataGridChanged}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGridComboBoxColumn>
<DataGridTextColumn Binding="{Binding Desc, Mode=TwoWay}" Header="Description"/>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="CurrentCellChanged">
<i:InvokeCommandAction Command="{Binding DataGridChanged}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
I know that there are similar questions on SO.
I have reviewed them, tried them, tried combinations of them, and retried them for the past day an half with very little success. I'm apparently dense or missing something.
The basic approaches covered were:
Placement Target
Using the Tag Attribute on a Parent
Using Ancestor Relative Source
Making use of ElementName root for the DataContext
Nothing seems to work and in only one instance am I even able to hit a break point in a Test Converter to see whats being bound. It will only work in the Tag Attribute of the DataCell. I am exasperated. This is as close as I have gotten:
<xcdg:DataGridControl.Resources>
<Style TargetType="{x:Type xcdg:DataCell}">
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource AncestorType={ x:Type xcdg:DataCell}}, Path=Tag, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</xcdg:DataGridControl.Resources>
As a variation on this approach I thought that sense the Binding on the Tag Attribute worked and was correct that I could use in the DataContext for the ContextMenu or the even just the MenuItem itself.
<ContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" StaysOpen="True">
or
<MenuItem Header="Acknowledge" DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" Command="{Binding Path=AcknowledgeViolationCommand}" />
With this next one I also tried PlacementTarget.DataContext and PlacementTarget.Tag with no results.
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
Other attempts:
<Setter Property="Tag" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType={ x:Type v:RTCA}}, Converter={StaticResource TestConverter}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem DataContext="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=DataContext}" Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
Here I tried making use of the ElementName root approach in different ways but it would only work in the Tag for the Cell and not the DataContext or Tag for the ContextMenu
<Setter Property="Tag" Value="{Binding DataContext, ElementName=root}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=PacementTarget.Tag.AcknowledgeViolationCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}, Converter={StaticResource TestConverter}}" />
</ContextMenu>
</Setter.Value>
</Setter>
<!-- variation of above -->
<Setter Property="Tag" Value="{Binding DataContext, ElementName=root}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Mode=Self}, Path=PlacementTarget.DataContext, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource TestConverter}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
I can list countless failed attempts. PlacementTargets, Tags, Ancestors, RelativeSources, why can't I make this work?. And why can I only seem to step into my TestConverter when its used on the DataCell Tag Binding and no where else?
Heres the Convert Method:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
When it fires for Tag binding I can actually see the ViewModel being passed just like I want and I can even execute my Command from the immediate window. Frankly I'm at my wits end on what should have been a 15 minute thing.
Can anyone tell me what I'm doing or wrong?
Additional Example(s)
From Lee O.s Comment I tried the BindingProxy approach without any luck.
<xcdg:DataGridControl.Resources>
<vl:BindingProxy x:Key="proxy" Data="{Binding }" />
<Style TargetType="{x:Type xcdg:DataCell}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{StaticResource proxy}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
Additional tries. These next attempts did do something new. Now at least when the app is started the breakpoint for the AcknowledgeViolationCommand fires once. However selecting the Item from the Context Menu still does nothing.
<ContextMenu DataContext="{Binding Data, Source={StaticResource proxy}}" StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=AcknowledgeViolationCommand}" />
</ContextMenu>
and
<ContextMenu StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=Data.AcknowledgeViolationCommand, Source={StaticResource proxy}}" />
</ContextMenu>
also tried x:Reference with the following:
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu StaysOpen="True">
<MenuItem Header="Acknowledge" Command="{Binding Path=DataContext.AcknowledgeViolationCommand, Source={x:Reference dummyControl}, Converter={StaticResource TestConverter}}" />
</ContextMenu>
</Setter.Value>
</Setter>
These approaches all have one thing in common in that they all seem to cause a breakpoint I have set in the command to hit ONCE as the grid loads. Further more adding the TestConverter to the reference and setting a breakpoint causes it to hit once for each row in the grid as it loads, but never when the command is clicked.
Here is the code for the Command in the ViewModel
public ICommand AcknowledgeViolationCommand
{
get
{
if (acknowledgeViolationCommand == null) // BreakPoint is here
acknowledgeViolationCommand = new RelayCommand(param => Test());
return acknowledgeViolationCommand;
}
}
private void Test()
{
string a = "a"; //Breakpoint here
}
Made a change to the command in case for some reason it was wanting the canExecute predicate. I confirmed the breakpoint for the canExecute method does fire.
public ICommand AcknowledgeViolationCommand
{
get
{
if (acknowledgeViolationCommand == null) // BP here
acknowledgeViolationCommand = new RelayCommand(param => Test(), param => YesDoIt());
return acknowledgeViolationCommand;
}
}
private bool YesDoIt()
{
return true; //BP here
}
I am currently displaying a hyperlink in my data grid with the following DataGridHyperLinkColumn definition:
<DataGridHyperlinkColumn Header="Item" Binding="{Binding Item, Mode=OneWay}">
<DataGridHyperlinkColumn.ElementStyle>
<Style>
<EventSetter Event="Hyperlink.Click" Handler="ButtonItemInfo_OnClick"/>
</Style>
</DataGridHyperlinkColumn.ElementStyle>
</DataGridHyperlinkColumn>
I want to change this to conditionally display a hyperlink or a label (or textblock). So if the bound value is "SH", I want to display the label. Otherwise I want the hyperlink.
How can I accomplish this?
I would bind the Hyperlink to a command and conditionally return false in CanExecute. You can style the hyperlink for disabled state.
<DataGrid ItemsSource="{Binding Items}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Item">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding DataContext.Navigate, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}">
<TextBlock Text="{Binding}" />
</Hyperlink>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
And the ViewModel:
public ObservableCollection<string> Items
{
get;
set;
}
public ICommand Navigate
{
get
{
return new RelayCommand(
(param) => DoNavigate(param as string), // execute
(param) => // can execute
{
var link = param as string;
return link != "SH";
});
}
}
If you really need a textbox for some rows you can use a DataTrigger in the columns cell style.
<DataGrid ItemsSource="{Binding Items}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Item">
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Template">
<Setter.Value>
<!-- Template for normal rows -->
<ControlTemplate>
<TextBlock>
<Hyperlink Command="{Binding DataContext.Navigate,
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}">
<TextBlock Text="{Binding}" />
</Hyperlink>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="SH">
<Setter Property="Template">
<Setter.Value>
<!-- Template for SH rows -->
<ControlTemplate>
<TextBlock Text="{Binding}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I have a DataTemplate that represents AppBar buttons that I declare through a collection of custom AppBarCommand objects.
public AppBarCommand(RelayCommand command, string buttonstyle)
{
Command = command;
ButtonStyle = buttonstyle;
}
<DataTemplate>
<Button Command="{Binding Command}"
Style="{Binding ButtonStyle, Converter={StaticResource StringNameToStyleConverter}}"/>
</DataTemplate>
I would like to add a CommandParameter binding, but the parameter has to be the Button itself. This is so I can set the PlacementTarget of a Callisto flyout. Is this possible?
<Button Command="{Binding Command}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}" />
Your Command property should be the generic version of the RelayCommand: RelayCommand<object> for instance.
Answer like Miklós Balogh said, or you can:
<Button x:Name="MyButton" Command="{Binding Command}" CommandParameter={Binding ElementName=MyButton ... />
I had the same problem but I used it in a bit different context:
<MenuItem ItemsSource="{Binding MyList}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding RelativeSource={ RelativeSource FindAncestor, AncestorType={ x:Type Window}}, Path= DataContext.MyListItemCommand}"/>
<Setter Property="CommandParameter" Value="{Binding}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
so I assume that even if you write it like this
<Button Command="{Binding Command}" CommandParameter="{Binding}" />
it should work.
I also recommend reading this post to understand it better.