一些WPF绑定问题

时间:2011-07-07 11:31:59

标签: wpf data-binding treeview

在我目前的项目中,我需要将日志输出显示到一个单独的窗口中,但无论出于何种原因我都无法使用它。由于我对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 :(得分:0)

据我所知,这不是你如何创建DependencyProperties。 DependencyObject是大多数UIElements的核心/基类,我会非常谨慎地将我的课程建立在它上面。

Google或在StackOverflow中搜索DependencyProperty,如果你真的想使用它。

在大多数情况下,使用INotifyPropertyChanged包装DomainModel / BusinessObject足以让WPF绑定拾取它