I have 3 RadioButtons in my app:
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Margin="91,206,0,24">
<TextBlock Text="Title Language:" VerticalAlignment="Top" FontSize="20" FontWeight="Bold" HorizontalAlignment="Left" Margin="0,0,15,0" />
<RadioButton x:Name="Rad_TitleNameRomaji" Content="Romaji" FontSize="20" Margin="0,0,10,0" Checked="TitleSettingsChanged"/>
<RadioButton x:Name="Rad_TitleNameEnglish" Content="English" FontSize="20" Margin="0,0,10,0" Checked="TitleSettingsChanged"/>
<RadioButton x:Name="Rad_TitleNameJapanese" Content="Japanese" FontSize="20" Checked="TitleSettingsChanged" />
</StackPanel>
There is a DataTemplate for my ListViewItems:
<DataTemplate x:Key="ItemTemplate_ListViewItems" >
<Grid Width="213" Height="326">
...
<TextBlock Text="{Binding WhatShouldIPutHere}) />
...
</Grid>
</DataTemplate>
The ListView's ItemsSource is a List<CustClass>.
CustClass:
public class CustClass : INotifyPropertyChanged
{
public string RomajiTitle { get; set; }
public string EnglishTitle { get; set; }
public string JapaneseTitle { get; set; }
...
public event PropertyChangedEventHandler PropertyChanged;
}
Now what i want is when I checked the "English" RadioButton, the Text of the TextBlock inside the DataTemplate will be bind to EnglishTitle. And so for the other two.
How can i approach this?
three DataTriggers should do the trick
<DataTemplate x:Key="ItemTemplate_ListViewItems" >
<Grid Width="213" Height="326">
<TextBlock >
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Rad_TitleNameRomaji,Path=IsChecked}" Value="True" >
<Setter Property="Text" Value="{Binding RomajiTitle}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Rad_TitleNameEnglish,Path=IsChecked}" Value="True" >
<Setter Property="Text" Value="{Binding EnglishTitle}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Rad_TitleNameJapanese,Path=IsChecked}" Value="True" >
<Setter Property="Text" Value="{Binding JapaneseTitle}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</DataTemplate>
Related
I am just wondering if there is a wpf combobox control that can contain multiple columns?
And if not, what XAML I need to use to achieve this?
I am just looking for a basic two column combobox if is possible,
Thanks
Please Refer these links for Multiple Column Combobox which is implemented by editing combox and comboboxitem Default template/style.
1)Link1
2)Link2
Xaml code : Please take a look at commented Trigger IsHighlighted in ComboboxItem style
<Grid>
<ComboBox Height="30" Margin="5" ItemsSource="{Binding}" HorizontalContentAlignment="Stretch">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Margin="2" Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid x:Name="gd" TextElement.Foreground="Black">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Margin="5" Grid.Column="0" Text="{Binding Name}"/>
<TextBlock Margin="5" Grid.Column="1" Text="{Binding State}"/>
<TextBlock Margin="5" Grid.Column="2" Text="{Binding Population}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ComboBoxItem.IsSelected" Value="True">
<Setter TargetName="gd" Property="Background" Value="Gray"></Setter>
<Setter TargetName="gd" Property="TextElement.Foreground" Value="White"></Setter>
</Trigger>
<Trigger Property="ComboBoxItem.IsMouseOver" Value="True">
<Setter TargetName="gd" Property="Background" Value="Blue"></Setter>
<Setter TargetName="gd" Property="TextElement.Foreground" Value="White"></Setter>
</Trigger>
<!--IsHighlighted and IsMouseOver is showing same effect but IsHighlighted is used for showing logical focus( for understanding check using tab key)-->
<!--<Trigger Property="ComboBoxItem.IsHighlighted" Value="True">
<Setter TargetName="gd" Property="Background" Value="Yellow"></Setter>
<Setter TargetName="gd" Property="TextElement.Foreground" Value="Black"></Setter>
</Trigger>-->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</Grid>
c# code
public partial class MainWindow : Window
{
private ObservableCollection<City> cities = new ObservableCollection<City>();
public MainWindow()
{
InitializeComponent();
cities.Add(new City() { Name = "Mumbai", State = "Maharashtra", Population = 3000000 });
cities.Add(new City() { Name = "Pune", State = "Maharashtra", Population = 7000000 });
cities.Add(new City() { Name = "Nashik", State = "Maharashtra", Population = 65000 });
cities.Add(new City() { Name = "Aurangabad", State = "Maharashtra", Population = 5000000 });
DataContext = cities;
}
}
class City
{
public string State { get; set; }
public string Name { get; set; }
public int Population { get; set; }
}
Output
Because I found, Heena, that your Xaml does not provide selected dropped down items to be highlighted I modified your code as follows:
Xaml
<ComboBox Name="cbCities" Height="30" Margin="5" HorizontalContentAlignment="Left" HorizontalAlignment="Stretch" ItemsSource="{Binding}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="2" Text="{Binding Name}"/>
<TextBlock Margin="2" Text="{Binding State}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border Name="templateBorder" Padding="2" SnapsToDevicePixels="true">
<ContentPresenter>
<ContentPresenter.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Margin="5" Grid.Column="0" Text="{Binding Name}"/>
<TextBlock Margin="5" Grid.Column="1" Text="{Binding State}"/>
<TextBlock Margin="5" Grid.Column="2" Text="{Binding Population}"/>
</Grid>
</ContentPresenter.Content>
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter Property="Foreground" Value="{x:Static SystemColors.HighlightTextBrush}"/>
<Setter TargetName="templateBorder" Property="Background" Value="{x:Static SystemColors.HighlightBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
C#
private void Window_Loaded(object sender, RoutedEventArgs e)
{
cities.Add(new City() { Name = "Boston", State = "MA", Population = 3000000 });
cities.Add(new City() { Name = "Los Angeles", State = "CA", Population = 7000000 });
cities.Add(new City() { Name = "Frederick", State = "MD", Population = 65000 });
cities.Add(new City() { Name = "Houston", State = "TX", Population = 5000000 });
cbCities.DataContext = cities;
}
class City
{
public string State { get; set; }
public string Name { get; set; }
public int Population { get; set; }
}
Output
I know im late but this is how you do it in a simplified way, After the DataTemplate tag you can put anything depending on how you want your lay out to look like.
<ComboBox.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="{StaticResource ForegroundMainBrush}"
Margin="5 0"
FontFamily="{StaticResource LatoBold}"
VerticalAlignment="Center">
<Run Text="Code :" />
<Run Text="{Binding ActivityCode,Mode=OneWay}" />
</TextBlock>
<TextBlock Foreground="{StaticResource ForegroundDarkBrush}"
Margin="5 0"
Text="|"
FontFamily="{StaticResource LatoBold}"
VerticalAlignment="Center" />
<TextBlock Foreground="{StaticResource ForegroundMainBrush}"
Margin="5 0"
FontFamily="{StaticResource LatoBold}"
VerticalAlignment="Center">
<Run Text="Rate :" />
<Run Text="{Binding Rate,Mode=OneWay}" />
</TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
Result
Sample Result
Or Use a readonly property in your DataModel as shown on the code below and set your combobox DisplayMemberPath to DisplayMemberPath="CodeRate"
public string ActivityCode { get; set; }
public string Rate { get; set; }
public string CodeRate => string.Format("Code: {0} | Rate:
{1}",ActivityCode,Rate);
I just use StackPanels in mine. Maybe kind of redundant per item, but it got me exactly the solution I wanted without getting too deep into things.
<ComboBox Name="cboTask">
<StackPanel Orientation="Horizontal">
<ComboBoxItem Name="someTask" Content="Doing Some Task" /><Button Name="cmdDetails" Content="..." />
</StackPanel>
</ComboBox>
I am trying to set a trigger in my DataTemplate so that when the source object has a property HasUnreadMessages set to true, then the header text goes red. But from inside the triggers in datatemplate I have no idea of how to reference the TextBlock of the header.
Full Code :
<Window x:Class="IRC.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:IRC"
Title="" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type loc:Tab}">
<DockPanel>
<ListBox x:Name="lstUsers" ItemsSource="{Binding Users}" Visibility="Collapsed" DockPanel.Dock="Right" />
<ListBox x:Name="lstMessage" ItemsSource="{Binding Messages}" />
</DockPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Type}" Value="Channel">
<Setter TargetName="lstUsers" Property="Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding HasUnreadMessages}" Value="True">
<Setter TargetName="tabHeader" Property="Foreground" Value="Red" /> // -- ERROR
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_Actions">
<MenuItem Header="_Connect" InputGestureText="Alt+C" Click="Connect" />
</MenuItem>
</Menu>
<TextBox Name="txtInput" Height="22" VerticalContentAlignment="Center" SpellCheck.IsEnabled="True" DockPanel.Dock="Bottom" />
<TabControl Name="pnlTabs" ItemsSource="{Binding}" ContentTemplate="{Binding}" DockPanel.Dock="Top">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="tabHeader" Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</DockPanel>
</Window>
Error :
The name "tabHeader" is not recognized.
The member "Foreground" is not recognized or is not accessible.
Tab :
public class Tab
{
public string Name { get; set; }
public string Type { get; set; }
public ObservableCollection<string> Users { get; set; }
public ObservableCollection<string> Messages { get; set; }
public bool HasUnreadMessages { get; set; }
}
You're trying to change a property of an object which is not known at that point.
Instead of putting the DataTrigger on the Tab, move the DataTrigger to the TabItem - since it's the TabItem that you're trying to change...
In your case, add a TabItem style:
<Window.Resources>
<DataTemplate DataType="{x:Type TabItem}" x:Key="ItemTemplateStyle">
<TextBlock x:Name="tabHeader" Text="{Binding Name}"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding HasUnreadMessages}" Value="True">
<Setter TargetName="tabHeader" Property="Foreground" Value="Red" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
And set this style for the TabItems:
<TabControl Name="pnlTabs" ItemsSource="{Binding}" ContentTemplate="{Binding}" DockPanel.Dock="Top"
ItemTemplate="{StaticResource ItemTemplateStyle}" />
I have a listview:
<ListView Name="SelectedFeeds">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}"
BasedOn="{StaticResource {x:Type ListViewItem}}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Name="panel">
<TextBlock x:Name="Title" FontSize="24"
Text="{Binding Title, IsAsync=True}" TextWrapping="Wrap" />
<Label x:Name="PubDate" FontSize="10"
Content="{Binding Path=PubDate, IsAsync=True}" />
<TextBlock my:HtmlParser.HTMLText=
"{Binding Path=Description, IsAsync=True}"
TextWrapping="Wrap"
Width="{Binding ElementName=panel,
Path=ActualWidth}" Height="0" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
There is a TextBlock in DataTemplate with large height.
At start I'm setting height of this textblock to "0" and when ListViewItem is selected, I need to set that height to "Auto".
This is probably can be done with triggers, but I can't figure it out.
For a trigger approach, use it in the DataTemplate.
<ListView Name="SelectedFeeds">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Name="panel">
<TextBlock x:Name="Title" FontSize="24"
Text="{Binding Title, IsAsync=True}" TextWrapping="Wrap" />
<Label x:Name="PubDate" FontSize="10"
Content="{Binding Path=PubDate, IsAsync=True}" />
<TextBlock x:Name="TextHolder"
my:HtmlParser.HTMLText=
"{Binding Path=Description, IsAsync=True}"
TextWrapping="Wrap"
Width="{Binding ElementName=panel,
Path=ActualWidth}" Height="0" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}" Value="True">
<Setter TargetName="TextHolder" Property="Height" Value="123"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Note the x:Name property on the TextBlock.
You may be able to do this with a trigger (although I don't know how at the moment), but you can do this with a converter instead.
public class BoolToLengthConverter : IValueConverter
{
public BoolToLengthConverter()
{
TrueValue = double.NaN;
FalseValue = 0;
}
[TypeConverter(typeof(LengthConverter))]
public double TrueValue { get; set; }
[TypeConverter(typeof(LengthConverter))]
public double FalseValue { get; set; }
#region Implementation of IValueConverter
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return System.Convert.ToBoolean(value) ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return TrueValue.Equals(value);
}
#endregion
}
Xaml
<ListView.Resources>
<Converters:BoolToLengthConverter
x:Key="BoolToHeightConverter" TrueValue="Auto" FalseValue="0" />
</ListView.Resources>
<TextBlock ... Height="{Binding Path=IsSelected,
Converter={StaticResource BoolToHeightConverter},
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListViewItem}}}"/>
You could of course achieve the same result by using a BoolToVisibilityConverter and binding to the TextBlock's Visibility property.
I have a ListView in which the ListView Items have button and textblock....
Senario :
I am able to click the button with out selecting the ListView Item i.e is the selection the last Item and then if i try to click the button of the first item the first time is not selected (In DataGrid it does select).
I Cannot use DataGrid as i am using CustomView in ListView.
If you need my code for reference of the problem i'll post it..
Any help in this regard would be great
My ListView :
<ListView Name="lv"
Grid.Row="1"
DisplayMemberPath="Name"
IsTextSearchEnabled="True"
ItemsSource="{Binding}"
KeyboardNavigation.DirectionalNavigation="Cycle"
SelectionMode="Single"
TextSearch.TextPath="{Binding Path=Person.Name}"
View="{Binding Path=SelectedItem,
ElementName=viewComboBox}" />
My DataTemplates for CustomViews :
<Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type CustomView:PlainView},
ResourceId=ImageView}"
BasedOn="{StaticResource {x:Type ListBox}}"
TargetType="{x:Type ListView}">
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value=".5" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="ItemContainerStyle" Value="{Binding (ListView.View).ItemContainerStyle, RelativeSource={RelativeSource Self}}" />
<Setter Property="ItemTemplate" Value="{Binding (ListView.View).ItemTemplate, RelativeSource={RelativeSource Self}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Name="bd"
Margin="{TemplateBinding Margin}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Margin="{TemplateBinding Padding}">
<WrapPanel KeyboardNavigation.DirectionalNavigation="Cycle"
Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
MinWidth="{Binding (ListView.View).MinWidth,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListView}}}"
IsItemsHost="True"
ItemWidth="{Binding (ListView.View).ItemWidth,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListView}}}" Orientation="Vertical"
Height="{Binding ActualHeight,
RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type CustomView:PlainView},
ResourceId=ImageViewItem}"
BasedOn="{StaticResource {x:Type ListBoxItem}}"
TargetType="{x:Type ListViewItem}">
<Setter Property="Padding" Value="3" />
<Setter Property="Margin" Value="5" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
<DataTemplate x:Key="centralTile">
<StackPanel Width="80" Height="40" KeyboardNavigation.AcceptsReturn="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button x:Name="tempabc" Command="{Binding Path=Launch}" KeyboardNavigation.AcceptsReturn="True" >
<TextBlock Text="{Binding Path=Name}" FocusManager.IsFocusScope="True"></TextBlock>
</Button>
<Image Grid.Column="1" Source="Water lilies.jpg"/>
</Grid>
<TextBlock
HorizontalAlignment="Center"
FontSize="13"
Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
<CustomView:PlainView x:Key="plainView"
ItemTemplate="{StaticResource ResourceKey=centralTile}"
ItemWidth="100" />
<GridView x:Key="myGridView">
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button>
<TextBlock Text="{Binding Path=Name}" />
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
As with most things, there are a number of ways to do this. Here's one I just threw together in a minute...
Given the following model:
public sealed class ItemModel
{
public string Name { get; set; }
}
I wish to display a collection of them to the user and select one via a button. This means I need three things in my ViewModel:
A collection of ItemModels
A "SelectedItem" property to hold the currently selected instance
An ICommand implementation to bind to the buttons in the View
I create my ViewModel and add these items to it. Please note, I prefer making my ViewModels extend DependencyObject rather than mess with INPC.
public sealed class ViewModel : DependencyObject
{
// 1. A collection of ItemModels
public ObservableCollection<ItemModel> ItemModels { get; private set; }
// 2. A "SelectedItem" property to hold the currently selected instance
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
"SelectedItem",
typeof(ItemModel),
typeof(ViewModel),
new UIPropertyMetadata(null));
public ItemModel SelectedItem
{
get { return (ItemModel)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
// 3. An ICommand implementation to bind to the buttons in the View
public Command SelectItem { get; private set; }
public ViewModel()
{
ItemModels = new ObservableCollection<ItemModel>();
ItemModels.Add(new ItemModel { Name = "One" });
ItemModels.Add(new ItemModel { Name = "Two" });
ItemModels.Add(new ItemModel { Name = "Three" });
SelectItem = new Command
{
ExecuteAction = x => SelectedItem = x as ItemModel
};
}
}
Lastly, I slap together my UI with a rudimentary ListView.
<Window
x:Class="q_7635202.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="WindowRoot">
<ListView
SelectedItem="{Binding SelectedItem}"
ItemsSource="{Binding ItemModels}">
<ListView.View>
<GridView>
<GridViewColumn
DisplayMemberBinding="{Binding Name}"
Header="Name" />
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button
Content="Select"
Command="{Binding DataContext.SelectItem,
ElementName=WindowRoot}"
CommandParameter="{Binding}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Window>
Its all pretty straight forward. I'm leaving out the ICommand implementation as it is trivial.
I'd like to display arbitrary XML in a TreeView, with expanding and collapsing nodes, showing both the element name and the set of attributes and their values. I think I can do this with HierarchicalDataTemplate .
I've seen the hints to use HierarchicalDataTemplate to display arbitrary XML elements, and text nodes, like this:
<Window.Resources>
<HierarchicalDataTemplate x:Key="NodeTemplate">
<TextBlock x:Name="tbName" Text="?" />
<HierarchicalDataTemplate.ItemsSource>
<Binding XPath="child::node()" />
</HierarchicalDataTemplate.ItemsSource>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Text">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Value}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Element">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Name}"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
<XmlDataProvider x:Key="xmlDataProvider">
</XmlDataProvider>
</Window.Resources>
....
<TreeView Name="treeView1"
ItemsSource="{Binding Source={StaticResource xmlDataProvider}, XPath=*}"
ItemTemplate= "{StaticResource NodeTemplate}"/>
Which works great. It displays the element names and text for each element. But my XML uses attributes to carry information. The schema is complex and I don't have a formal definition of it, so for now I am treating it as arbitrary XML.
The simplest document looks like this:
<c4soap name="GetVersionInfo" seq="" result="1">
<versions>
<version name="Director"
version="2.1.0.126418"
buildtype=""
builddate="Jun 1 2011" buildtime="14:52:43" />
<version name="MediaManager"
version="2.1.0.126418"
buildtype=""
builddate="Jun 1 2011"
buildtime="14:36:17" />
</versions>
</c4soap>
Using the above HierarchicalDataTemplate definition, I get this for a display:
Not quite what I want. For each node I want to display both the element name and the set of attributes and their values.
I tried this:
<Window.Resources>
<HierarchicalDataTemplate x:Key="NodeTemplate">
<WrapPanel
Focusable="False">
<TextBlock x:Name="tbName" Text="?" />
<TextBlock x:Name="tbAttrs" Text="?" />
</WrapPanel>
<HierarchicalDataTemplate.ItemsSource>
<Binding XPath="child::node()" />
</HierarchicalDataTemplate.ItemsSource>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Text">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Value}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Element">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Name}"/>
<Setter TargetName="tbAttrs" Property="Text" Value="{Binding Path=Attributes}"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
<XmlDataProvider x:Key="xmlDataProvider">
</XmlDataProvider>
</Window.Resources>
... which gets me kinda close, but the
Value="{Binding Path=Attributes}" results in a display of "(Collection)" in the TreeView.
How can I simply display all the actual attribute names and values, in addition to the element name?
I added an ItemsControl into the template, like this :
<Window.Resources>
<SolidColorBrush x:Key="xmlValueBrush" Color="Blue" />
<SolidColorBrush x:Key="xmAttributeBrush" Color="Red" />
<SolidColorBrush x:Key="xmlTagBrush" Color="DarkMagenta" />
<SolidColorBrush x:Key="xmlMarkBrush" Color="Blue" />
<DataTemplate x:Key="attributeTemplate">
<StackPanel Orientation="Horizontal"
Margin="3,0,0,0"
HorizontalAlignment="Center">
<TextBlock Text="{Binding Path=Name}"
Foreground="{StaticResource xmAttributeBrush}"/>
<TextBlock Text="=""
Foreground="{StaticResource xmlMarkBrush}"/>
<TextBlock Text="{Binding Path=Value}"
Foreground="{StaticResource xmlValueBrush}"/>
<TextBlock Text="""
Foreground="{StaticResource xmlMarkBrush}"/>
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="nodeTemplate">
<StackPanel Orientation="Horizontal"
Focusable="False">
<TextBlock x:Name="tbName" Text="?" />
<ItemsControl
ItemTemplate="{StaticResource attributeTemplate}"
ItemsSource="{Binding Path=Attributes}"
HorizontalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
<HierarchicalDataTemplate.ItemsSource>
<Binding XPath="child::node()" />
</HierarchicalDataTemplate.ItemsSource>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Text">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Value}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Element">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Name}"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
<XmlDataProvider x:Key="xmlDataProvider">
</XmlDataProvider>
</Window.Resources>
Now it displays element names and the set of attributes and their values, like this:
you also can use a template selector for the different node types and use the XPath node()|#* to loop thru all types of nodes:
<TreeView
x:Name="TreeView"
ItemsSource="{Binding}"
ItemTemplateSelector="{DynamicResource ResourceKey=NodeTemplateSelector}">
<TreeView.Resources>
<HierarchicalDataTemplate x:Key="TextTemplate">
<Grid>
<uixml:TextInputControl DataContext="{Binding}" />
</Grid>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="AttributeTemplate">
<Grid>
<uixml:AttributeInputControl DataContext="{Binding}" />
</Grid>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="NodeTemplate" >
<TextBlock Text="{Binding Path=Name}" />
<HierarchicalDataTemplate.ItemsSource>
<Binding XPath="child::node()|#*" />
</HierarchicalDataTemplate.ItemsSource>
</HierarchicalDataTemplate>
<ui:XmlTemplateSelector
x:Key="NodeTemplateSelector"
NodeTemplate="{StaticResource NodeTemplate}"
TextTemplate="{StaticResource TextTemplate}"
AttributeTemplate="{StaticResource AttributeTemplate}" />
</TreeView.Resources>
</TreeView>
and :
public class XmlTemplateSelector:DataTemplateSelector{
public DataTemplate NodeTemplate { get; set; }
public DataTemplate TextTemplate { get; set; }
public DataTemplate AttributeTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
XmlNode node = (XmlNode)item;
switch (node.NodeType) {
case XmlNodeType.Attribute:
return AttributeTemplate;
case XmlNodeType.Element:
return NodeTemplate;
case XmlNodeType.Text:
return TextTemplate;
}
throw new NotImplementedException(String.Format("not implemented for type {0}", node.NodeType));
}
}