Некоторые обязательные вопросы WPF - PullRequest
1 голос
/ 07 июля 2011

В моем текущем проекте мне нужно отобразить вывод журнала в отдельном окне, но по какой-то причине я не могу заставить его работать.Поскольку я довольно новичок в связывании данных WPF, я подозреваю, что проблема во мне; O).

Вот как это должно работать:

Есть три класса модели представления, каждый из которых представляет собой агрегатдля другого (переваривается для ясности)

// The VM starting point, using a Singleton instance
// It holds a collection of log "sources", each representing a separate class' logging
internal sealed class ClassLogVM : DependencyObject
{
    private readonly Dictionary<object, ClassLogSourceVM> _sourceIndex;
    private readonly object _syncRoot;

    /* dependency property initialization omitted */

    private static ClassLogVM _s_singleton;
    public static ClassLogVM Singleton
    {
        get { return _s_singleton ?? (_s_singleton = new ClassLogVM()); }
    }

    public IEnumerable<ClassLogSourceVM> Items
    {
        get { return (IEnumerable<ClassLogSourceVM>) GetValue(_s_itemsProp); }
        set { SetValue(_s_itemsProp, value); }
    }

    private void classLogLogged(object sender, ClassLogEventArgs args)
    {
        VM.InvokeInUiThread(() =>
        {
            ClassLogSourceVM source;
            var caller = args.Caller == null ? "(unknown)" : args.Caller.ToString();
            lock (_syncRoot)
                if (!_sourceIndex.ContainsKey(caller))
                {
                    source = new ClassLogSourceVM(caller);
                    ((List<ClassLogSourceVM>) Items).Add(source);
                    _sourceIndex.Add(caller, source);
                }
                else
                    source = _sourceIndex[caller];
            source.Add(new ClassLogItemVM(args.Timestamp, args.Message));
        });
    }

    private ClassLogVM()
    {
        _syncRoot = new object();
        _sourceIndex = new Dictionary<object, ClassLogSourceVM>();
        Items = new List<ClassLogSourceVM>();
        ClassLog.Logged += classLogLogged;
    }
}

// represents a collection of log entries (see: Items property)
public class ClassLogSourceVM : DependencyObject
{
    public string Name
    {
        get { return (string) GetValue(_s_nameProp); }
        set { SetValue(_s_nameProp, value); }
    }

    public IEnumerable<ClassLogItemVM> Items
    {
        get { return (IEnumerable<ClassLogItemVM>)GetValue(_s_itemsProp); }
        private set { SetValue(_s_itemsProp, value); }
    }

    public void Add(ClassLogItemVM item)
    {
        ((List<ClassLogItemVM>)Items).Add(item);
    }

    public override string ToString()
    {
        return GetValue(_s_nameProp) + " (Count: " + Items.Count() + ")";
    }

    public ClassLogSourceVM(string name)
    {
        if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
        SetValue(_s_nameProp, name);
        Items = new List<ClassLogItemVM>();
    }
}

// represents an individual log entry
public sealed class ClassLogItemVM : DependencyObject
{
    public DateTime Timestamp
    {
        get { return (DateTime) GetValue(_s_timestampProp); }
    }

    public string Message
    {
        get { return (string) GetValue(_s_messageProp); }
        set { SetValue(_s_messageProp, value); }
    }

    public override string ToString()
    {
        return Timestamp.ToString("HH:mm:ss:fff") + ": " + Message;
    }

    public ClassLogItemVM(DateTime timestamp, string message)
    {
        SetValue(_s_timestampProp, timestamp);
        SetValue(_s_messageProp, message);
    }
}

Так я настраивал XAML (пользовательский элемент управления под названием "ClassLogView") ...

<UserControl x:Class="GeDevelop.GVSViewer.Views.Debug.ClassLogView"
         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:Debug="clr-namespace:GeDevelop.GVSViewer.ViewModel.Debug" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300" 
         DataContext="{Binding Debug:ClassLogVM.Singleton.Items}">
<UserControl.Resources>
    <CollectionViewSource x:Key="sources" Source="{Binding}">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="Source" />
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
    <DataTemplate DataType="{x:Type Debug:ClassLogSourceVM}">
        <TextBlock Text="{Binding Name}" />
    </DataTemplate>
</UserControl.Resources>
<Grid d:DataContext="{Binding Debug:ClassLogVM.Singleton}">
    <TreeView ItemsSource="{Binding Source={StaticResource sources}}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate DataType="{x:Type Debug:ClassLogSourceVM}" ItemsSource="{Binding Items}">
                <TextBlock Text="{Binding}"/>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
</Grid>

В результате Treeview пусто.

Теперь мои вопросы: 1. Что я пропустил, из-за чего TreeView ничего не отображало?2. Мне нужно программно назначить DataContext пользовательскому элементу управления, несмотря на то, что я указал это в теге.Почему?

Буду признателен за объяснение.WPF точно не для слабонервных ...; O)

1 Ответ

0 голосов
/ 07 июля 2011

насколько я знаю, это не то, как вы создаете DependencyProperties. DependencyObject является базовым / базовым классом большинства UIElements, и я бы очень осторожно основал на нем свой класс.

Google или поиск в DeckdencyProperty в StackOverflow, если вы действительно хотите его использовать.

В большинстве случаев обертывание вашего DomainModel / BusinessObject с INotifyPropertyChanged более чем достаточно, чтобы привязка WPF подняла его

...