1

This is my simplified XAML:

<Window x:Class="MyForm.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyForm"
        Title="MyForm" Closing="Window_Closing" Icon="Images\main.ico" Height="633" Width="1602" Loaded="Window_Loaded" xmlns:my="clr-namespace:MyForm">
    <Window.Resources>
        <ObjectDataProvider x:Key="dataSetProvider" MethodName="CreateDataSet" ObjectType="{x:Type local:DataSetCreator}" />
        <DataTemplate x:Key="ItemTemplate">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding item_title}">
                </TextBlock>
            </StackPanel>
        </DataTemplate>
        <HierarchicalDataTemplate x:Key="FeedTemplate" ItemsSource="{Binding FK_FM_FEEDS_FEED_0_0}" ItemTemplate="{StaticResource ItemTemplate}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding feed_title}">
                </TextBlock>
            </StackPanel>
        </HierarchicalDataTemplate>
    </Window.Resources>
    <Grid>
        <TreeView LayoutUpdated="treeViewMain_LayoutUpdated" TreeViewItem.Expanded="TextBlock_Expanded" Name="treeViewMain" DataContext="{StaticResource dataSetProvider}" ItemsSource="{Binding FM_FEEDS_FEEDS}" ItemTemplate="{StaticResource FeedTemplate}">
        </TreeView>
    </Grid>
</Window>

The binding works as expected, the feed_title from the parent table in the dataset is bound to the top node in the treeview. Then all the items in that specific feed (childtable of dataset) is bound as child-nodes to that top node in the treeview.

What I'm trying to achieve: I would like to add some more information to the top node than just the feed_title. I would like to read from the child table which items are synchronized or not, and update the top node to contain text like: "(4/10) Title". So that it informs user about that only 4 out of the 10 items are synchronized. I do not need the solution, but would like to know how to go about this...

jmelhus
  • 1,130
  • 2
  • 12
  • 28
  • To archive such flexibility you will need additional level of abstraction here via view-model classes with additional UI-related logic. They will sits between you original DataSet object(s) and the UI elements (declared in XAML). – Sevenate Jun 19 '13 at 14:19

3 Answers3

2

Just to illustrate how approach suggested by @mikehc will work take a look at the following working code sample:

FeedViewModel.cs - Notice that it use timer for periodical refresh (1 sec in this code or whatever you like/need in yours) of its title with information from its child's synchronization status. This is much simpler solution rather than subscribing to events from each of its child to be notified when it will be synchronized, deal with adding new and removing old child items and so on:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Threading;

namespace WpfApplication11
{
    public class FeedViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged = delegate {};

        DispatcherTimer timer =
                        new DispatcherTimer(DispatcherPriority.Background);

        private string feedName;

        public string FeedName
        {
            get { return feedName; }
            set
            {
                feedName = value;
                PropertyChanged(this,
                                    new PropertyChangedEventArgs("FeedName"));
                PropertyChanged(this,
                                    new PropertyChangedEventArgs("FeedTitle"));
            }
        }

        public ObservableCollection<ItemViewModel> FeedItems { get; set; }

        public string FeedTitle
        {
            get
            {
                return string.Format("({0}/{1}) {2}",
                        FeedItems.Count(item => item.IsSynchronized),
                        FeedItems.Count,
                        FeedName);
            }
        }

        public FeedViewModel()
        {
            FeedItems = new ObservableCollection<ItemViewModel>();

            timer.Interval = TimeSpan.FromMilliseconds(1000);
            timer.Tick += (sender, args) =>
                    PropertyChanged(this,
                                   new PropertyChangedEventArgs("FeedTitle"));
            timer.Start();
        }
    }
}

ItemViewModel.cs:

using System.ComponentModel;

namespace WpfApplication11
{
    public class ItemViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged = delegate {};

        private string itemName;
        private bool isSynchronized;

        public string ItemName
        {
            get { return itemName; }
            set
            {
                itemName = value;
                PropertyChanged(this, new PropertyChangedEventArgs("ItemName"));
            }
        }

        public bool IsSynchronized
        {
            get { return isSynchronized; }
            set
            {
                isSynchronized = value;
                PropertyChanged(this,
                                new PropertyChangedEventArgs("IsSynchronized"));
            }
        }
    }
}

DataSetCreator.cs - this is just a two mocks - DataSet and DataSetCreator for classes that you mentioned in your code, so feel free to use you own):

using System.Collections.ObjectModel;

namespace WpfApplication11
{
    public class DataSet
    {
        public ObservableCollection<FeedViewModel> Feeds { get; private set; }

        public DataSet()
        {
            Feeds = new ObservableCollection<FeedViewModel>
            {
                new FeedViewModel
                {
                    FeedName = "Feed #1",
                    FeedItems = new ObservableCollection<ItemViewModel>
                    {
                        new ItemViewModel
                        {
                            ItemName = "Item #1.1",
                            IsSynchronized = true
                        },
                        new ItemViewModel
                        {
                            ItemName = "Item #1.2",
                            IsSynchronized = true
                        },
                        new ItemViewModel
                        {
                            ItemName = "Item #1.3",
                            IsSynchronized = false
                        },
                    }
                },
                new FeedViewModel
                {
                    FeedName = "Feed #2",
                    FeedItems = new ObservableCollection<ItemViewModel>
                    {
                        new ItemViewModel
                        {
                            ItemName = "Item #2.1",
                            IsSynchronized = true
                        },
                        new ItemViewModel
                        {
                            ItemName = "Item #2.2",
                            IsSynchronized = true
                        },
                    }
                },
                new FeedViewModel
                {
                    FeedName = "Feed #3",
                    FeedItems = new ObservableCollection<ItemViewModel>
                    {
                        new ItemViewModel
                        {
                            ItemName = "Item #3.1",
                            IsSynchronized = false
                        },
                        new ItemViewModel
                        {
                            ItemName = "Item #3.2",
                            IsSynchronized = false
                        },
                    }
                }
            };
        }
    }

    public class DataSetCreator
    {
        public DataSet CreateDataSet()
        {
            return new DataSet();
        }
    }
}

and, finally, MainWindow.xaml - updated to use the mock class DataSetCreator.cs from above:

<Window x:Class="WpfApplication11.MainWindow"
        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" 
        xmlns:local="clr-namespace:WpfApplication11"
        mc:Ignorable="d" Title="MainWindow" Height="350" Width="525">
    <Window.Resources>

        <local:ItemViewModel x:Key="DesignItemViewModel" />
        <local:FeedViewModel x:Key="DesignFeedViewModel" />

        <ObjectDataProvider x:Key="dataSetProvider"
                            ObjectType="{x:Type local:DataSetCreator}"
                            MethodName="CreateDataSet" />

        <DataTemplate x:Key="ItemTemplate">
            <StackPanel Orientation="Horizontal"
                        d:DataContext="{StaticResource DesignItemViewModel}">
                <TextBlock Text="{Binding Path=ItemName}" />
                <CheckBox IsChecked="{Binding Path=IsSynchronized}" 
                                    Margin="25,0,0,0"
                          Content=" is synchronized?"/>
            </StackPanel>
        </DataTemplate>

        <HierarchicalDataTemplate x:Key="FeedTemplate"
                                  ItemsSource="{Binding Path=FeedItems}"
                                  ItemTemplate="{StaticResource ItemTemplate}">

            <StackPanel Orientation="Horizontal"
                        d:DataContext="{StaticResource DesignFeedViewModel}">
                <TextBlock Text="{Binding Path=FeedTitle}" />
            </StackPanel>

        </HierarchicalDataTemplate>

    </Window.Resources>

    <Grid>
        <TreeView DataContext="{StaticResource dataSetProvider}"
                  d:DataContext="{StaticResource dataSetProvider}"
                  ItemsSource="{Binding Path=Feeds}"
                  ItemTemplate="{StaticResource FeedTemplate}" />
    </Grid>

</Window>
Sevenate
  • 6,221
  • 3
  • 49
  • 75
1

I recommend you to build a custom model or viewModel to handle these complex relationships between child and parents. For example:

  1. Create two classes named Parent and Child.
  2. Each classes have properties to keep their corresponding datasets.
  3. Parent have a collection of their child.
  4. Child implements INotifyPropertyChanged so the parents can subscribe to the PropertyChanged event and know when a child has been read (and keep track of that).

At the end you bind to the classes properties and not to the datasets. Remember to implement INotifyPropertyChanged in the properties you want to bind or use DependencyProperties.

Here are some tricks about implementing INotifyPropertyChanged

Community
  • 1
  • 1
mikehc
  • 999
  • 8
  • 22
0

I think your model or viewmodel layer have some property like "IsSynchronized" to indicate its status. If it is that condition, I suggest you using Converter (maybe inherite from IValueConverter) in binding. In the converter implemention, you can get all children's sync status and return calculated number to UI element.

Certainly, you must implement INotifyPropertyChanged interface.