Trouble Binding CommandParameter using XPath within HierarchicalDataTemplate
I am relatively new to WPF and I have run into a little problem. Using HierarchicalDataTemplates, I have successfully bound XML to a TreeView control. Each node renderers correctly (including the Label in the SubstepTemplate).
I can even get the bound Command in my ViewModel from the Button in the SubstepTemplate, but only if I enter a hardcoded value for the CommandParameter (e.g. 999). All attempts at binding to the commandID attribute in my XML have failed.
Here is what I have right now:
XML:
<root xmlns="">
<step label="Step Label 1">
<button label="Button Title 1A" commandID="701" />
<button label="Button Title 1B" commandID="702" />
<button label="Button Title 1C" commandID="703" />
</step>
<step label="Step Label 2">
<button label="Button Title 2A" commandID="801" />
<button label="Button Title 2B" commandID="802" />
</step>
</root>
XAML:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SidePanel">
<HierarchicalDataTemplate
x:Key="SubstepTemplate"
DataType="button"
ItemsSource="{Binding XPath=*}">
<StackPanel Orientation="Horizontal">
<Button Margin="2" Width="32" Height="32" Command="{Binding ElementName=MyTreeView, Path=DataCont开发者_如何学Cext.PluginCommand}" CommandParameter="{Binding XPath=@commandID}" />
<Label VerticalAlignment="Center" Margin="8,0,0,0" Content="{Binding XPath=@label}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
x:Key="StepTemplate"
DataType="step"
ItemsSource="{Binding XPath=*}"
ItemTemplate="{StaticResource SubstepTemplate}">
<Expander Header="{Binding Mode=OneWay, XPath=@label}" HorizontalAlignment="Stretch">
</Expander>
</HierarchicalDataTemplate>
<Style TargetType="{x:Type local:SidePanelControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SidePanelControl}">
<TreeView
Name="MyTreeView"
ItemsSource="{Binding XmlRoot}"
ItemTemplate="{StaticResource StepTemplate}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
HorizontalAlignment="Stretch">
</TreeView>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
ViewModel Snippet:
public DelegateCommand<string> PluginCommand
{
get
{
if (pluginCommand == null)
{
pluginCommand = new DelegateCommand<string>(ExecPluginCommand, canExecPluginCommand);
}
return pluginCommand;
}
}
public void ExecPluginCommand(string param)
{
LogMessage("In ExecPluginCommand, param = " + param);
}
public bool canExecPluginCommand(string param)
{
return true;
}
What is the correct Binding expression that needs to go in the CommandParameter to get this to work? {Binding XPath=@commandID} doesn't seem to work, and I do not understand why not.
I found the problem. When an XPath is used as a CommandParameter binding, the templated DelegateCommand receives a parameter of type System.Xml.XmlNode, not string.
I was getting an exception in my Command when attempting to get the command data type
void ICommand.Execute(object parameter)
{
TParam value = GetCommandDataType(parameter);
Execute(value);
}
because TypeConverter::CanConvertFrom did not know what to do with an argument of type System.Xml.XmlNode.
I was able to get the commandID value by testing explicitly for
data is System.Xml.XmlNode
in GetCommandDataType(object data) and then returning the .Value member, i.e.
else if (data is System.Xml.XmlNode)
{
System.Xml.XmlNode foo = (System.Xml.XmlNode)data;
string fooVal = foo.Value;
TypeConverter converter = TypeDescriptor.GetConverter(typeof(string));
result = (TParam)converter.ConvertFrom(fooVal);
}
精彩评论