开发者

.Net 4 Binding To WPF Cotrol in ElementHost (MVVM)

I've got a WPF ElementHost in a Winforms app. The User Control has some text and a TreeView which should show a tree of available commands provided by the app.

I'm new to WPF (this is a learning exercise) so am having some problems binding the data.

I've created a CommandTreeViewModel class to act as my view model. It has a FirstGeneration property which is an IEnumerable(of CommandViewModel). The CommandViewModel class in turn has some simple properties describing the Command including a Children property (again an IEnumerable(of CommandViewModel)).

I've added a Public Property ViewModel As CommandTreeViewModel to my WPF User Control which is set by my winforms app for now.

What I don't know how to do is take that data I've been handed in the ViewModel property and bind it to the TreeView. (And is there any way to strongly type my XAML Binding's ViewModel Class a-la MVC?)

I've included what I believe is the pertinent code below in case it's needed.

User Control

Public Class WPFCommandTree
    Implements INotifyPropertyChanged

    Public Property ViewModel As CommandTreeViewModel
        Get
            Return DirectCast(GetValue(ViewModelProperty), CommandTreeViewModel)
        End Get

        Set(ByVal value As CommandTreeViewModel)
            If Not value.Equals(DirectCast(GetValue(ViewModelProperty), CommandTreeViewModel)) Then
                SetValue(ViewModelProperty, value)
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("ViewModelProperty"))
            End If
        End Set
    End Property

    Public Shared ReadOnly ViewModelProperty As DependencyProperty = _
      DependencyProperty.Register("ViewModel",
      GetType(CommandTreeViewModel), GetType(Window),
      New FrameworkPropertyMetadata(Nothing))


    Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class

XAML

<UserControl x:Class="WPFCommandTree"
             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="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinit开发者_如何学运维ions>
            <RowDefinition Height="60" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBlock FontSize="30" HorizontalAlignment="Center" VerticalAlignment="Center">Test</TextBlock>
        <TreeView ItemsSource="{Binding FirstGeneration}"
                  VerticalAlignment="Stretch"
                  HorizontalAlignment="Stretch"
                  Grid.Row="1"
                  DataContext="{Binding}">
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded"
                            Value="{Binding IsExpanded, Mode=TwoWay}" />
                    <Setter Property="IsSelected"
                            Value="{Binding IsSelected, Mode=TwoWay}" />
                    <Setter Property="FontWeight"
                            Value="Normal" />
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="FontWeight" Value="Bold" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </TreeView.ItemContainerStyle>
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</UserControl>

View Models

Public Class CommandTreeViewModel
    Public Property RootCommand As CommandViewModel
    Public Property FirstGeneration As ReadOnlyCollection(Of CommandViewModel)

    Public Sub New(ByVal RootCommand As Command)
        _RootCommand = New CommandViewModel(RootCommand)
        _FirstGeneration = New ReadOnlyCollection(Of CommandViewModel)(New CommandViewModel() {_RootCommand})
    End Sub

    Public Sub New(ByVal RootCommand As CommandViewModel)
        Me.RootCommand = RootCommand
        Me.FirstGeneration = New ReadOnlyCollection(Of CommandViewModel)(New CommandViewModel() {_RootCommand})
    End Sub

    Public Sub New(ByVal RootCommands As IEnumerable(Of CommandViewModel))
        Me.RootCommand = RootCommands.First
        Me.FirstGeneration = New ReadOnlyCollection(Of CommandViewModel)(RootCommands.ToList)
    End Sub
End Class


Public Class CommandViewModel
    Implements INotifyPropertyChanged

    Public Property Command As Command
    Public Property Children As ReadOnlyCollection(Of CommandViewModel)
    Public Property Parent As CommandViewModel
    Public Property Name As String

    Private Property _IsSelected As Boolean
    Public Property IsSelected() As Boolean
        Get
            Return _isSelected
        End Get
        Set(ByVal value As Boolean)
            If value <> _isSelected Then
                _isSelected = value
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("IsSelected"))
            End If
        End Set
    End Property

    Private Property _IsExpanded As Boolean
    Public Property IsExpanded() As Boolean
        Get
            Return _IsExpanded
        End Get
        Set(ByVal value As Boolean)
            If value <> IsExpanded Then
                _IsExpanded = value
                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("IsExpanded"))
                If _IsExpanded And _Parent IsNot Nothing Then
                    _Parent.IsExpanded = True
                End If
            End If
        End Set
    End Property

    Public Sub New(ByVal Command As Command)
        Me.New(Command, Nothing)
    End Sub


    Private Sub New(ByVal Command As Command, ByVal Parent As CommandViewModel)
        _Command = Command
        _Parent = Parent

        If Command.Children IsNot Nothing AndAlso Command.Children.Count > 0 Then
            _Children = New ReadOnlyCollection(Of CommandViewModel)(
             Command.Children.Select(Function(x) New CommandViewModel(x, Me)
            ).ToList)
        End If
    End Sub

    Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class

As you can see, I'm not really clear on what I need to do and have been trying to hack some code together from various tutorials. Most are WPF only.

Running the above loads the cotrol correctly (I can see the "Test" text on my form) but the TreeView remains blank. No errors are thrown. I'm assuming this is because I haven't bound the data correctly.

Finally, I' also unclear on which of my properties need to be DPs? the User Control, ViewModel, Model Children? I'm just not clear on how the binding works.


It's a lot of code to parse, and I haven't coded in VB for a while, but I see at least a few things.

I would recommend you start a little more simply. If you are really just getting started, start out by trying to get something simple to bind, not a treeview with multiple levels of commands. Try getting a flat datagrid to bind before going for a hierarchical data template, adding styles (with triggers!), etc. You are introducing too many concepts at once IMHO.

I don't think you need to be using Dependency Properties at all here, unless I am missing something. Dependency properties are mainly used when you want to drop a usercontrol into another control/window and have the ability to control the property via XAML/Databinding.

It looks like you are trying to set your viewModel via a dependency property. Instead, just set the DataContext property in the constructor on your codebehind--something like Me.DataContext = New CommandTreeViewModel (remember my VB is rusty :) ). This is probably your main problem with the binding not working, actually, as the View's DataContext is not getting set.

Finally, your debug output window in VS should contain any databinding errors, which is a big help in figuring out where binding is failing.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜