Hello,
I am working on a customised control which is, simply put, a
TreeView
with
ChecBox
es. The
TreeView
resides inside a
ComboBox
.
The
TreeView
being inside a combobox is just a part of the requirement and I have already achieved the relation I wanted between them. My problem starts with binding
TreeViewItem
s with two different bindings. I have reached the current state after a lot of reading and googling. And am really stuck, hence the question here.
The data to back the
TreeView
is a XML. This XML is loaded in the
ComboBox
as a string
DependencyProperty
named as 'Value' and the
TreeView
binds to that
DependencyProperty
of the
ComboBox
. All this is being done in the UI layer because I believe that this is part of the behavior of the custom control that I am creating and hence no view model backing the view. I am willing to stand corrected here.
Here is my XAML
<ComboBox x:Class="HierarchicalCheckComboBox.HCCB"
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="30" d:DesignWidth="300"
DropDownClosed="ComboBox_DropDownClosed">
<ComboBox.Resources>
<HierarchicalDataTemplate x:Key="NodeTemplate"
ItemsSource="{Binding XPath=child::node()}">
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="checkBox" IsChecked="{Binding XPath=@checked}" />
<TextBlock x:Name="text" Text="{Binding XPath=@text}" />
</StackPanel>
</HierarchicalDataTemplate>
<XmlDataProvider x:Key="xmlDataProvider" />
</ComboBox.Resources>
<ComboBoxItem MaxHeight="100" MinHeight="25" >
<TreeView Name="treeView1"
ItemsSource="{Binding Source={StaticResource xmlDataProvider}, XPath=/root/child}"
ItemTemplate="{StaticResource NodeTemplate}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</ComboBoxItem>
</ComboBox>
This is the partial code behind class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using System.IO;
namespace HierarchicalCheckComboBox
{
public partial class HCCB : ComboBox
{
bool _settingState = false;
public static readonly DependencyProperty ValueProperty;
static HCCB ()
{
ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(HCCB),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsArrange, new PropertyChangedCallback(OnValueChanged)));
}
public string Value
{
get { return (string)GetValue(ValueProperty); }
set
{
if (Value.Equals(value))
return;
SetValue(ValueProperty, value);
}
}
public HCCB()
{
InitializeComponent();
}
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
HCCB hccb = d as HCCB;
if (hccb == null || hccb._settingState)
return;
string newValue = e.NewValue as string, oldValue = e.OldValue as string;
if (newValue == null || newValue.Equals(oldValue))
return;
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = false;
document.LoadXml(newValue);
XmlDataProvider dataProvider = hccb.FindResource("xmlDataProvider") as XmlDataProvider;
dataProvider.Document = document;
}
private void ComboBox_DropDownClosed(object sender, EventArgs e)
{
HCCB hccb = sender as HCCB;
if (hccb == null)
return;
XmlDataProvider dataProvider = hccb.FindResource("xmlDataProvider") as XmlDataProvider;
XmlDocument document = dataProvider.Document;
string newXml = document.OuterXml;
if (!Value.Equals(newXml))
{
try
{
_settingState = true;
Value = newXml;
}
finally
{
_settingState = false;
}
}
}
}
}
This is how a sample XML, which is set on the 'Value' property of the
ComboBox
looks:
="1.0"="utf-16"
<root>
<child text="text1" checked="false">
<child text="text11" checked="true" />
</child>
<child text="text2" checked="true">
<child text="text21" checked="false" />
<child text="text22" checked="true" />
</child>
<child text="text3" checked="false" >
<child text="text33" checked="false" >
<child text="text333" checked="true" >
<child text="text3333" checked="false" >
</child>
</child>
</child>
</child>
</root>
Now, I want to create a parent child relation between the
CheckBox
ex. They are not tri-state. So, when I check a
CheckBox
of a
TreeViewItem
all the
CheckBox
es of the child
TreeViewItem
s should be checked. Same for uncheck. I referred to many sources on the net to get an idea of how that can be done. The answer by Dr. WPF in
this comes the closest to what I need.
But, I am still at loss as to how will I bind the
AttachedProperty
of the
TreeViewItem
to
XPath
as well as an implementation to set the corresponding
AttachedProperty
of the child element.
This article uses the view model to achieve the same.
One option I have is to follow a combination of
this and the previous article to first create a view model from the XML using LINQ. And then use that view model to do the parent child relation. Something similar is done
here which uses a custom library. But, is there no other way to achieve this?
These are the articles I referred to get where I got.
.
WPF-XmlDataProvider-Two-Way-Data-Binding
.
Two-way binding of Xml data to the WPF TreeView
.
WPF: XmlDataProvider Two-Way Data Binding
.
WPF: Visualizing arbitrary XML documents in a TreeView control