将ItemsControl中的viewmodel绑定到自定义用户控件

时间:2016-02-26 12:17:23

标签: c# wpf data-binding

我正在使应用程序(从Windows Forms和VB.Net到WPF)现代化,该应用程序向用户显示最近消息的列表。我创建了MessageViewModelMessagesListViewModel和两个自定义用户控件MessageListUserControlMessageUserControl

使用ItemsControl在垂直堆栈面板中的MessageListUserControl列出消息,并使用MessageuserControl进行渲染。但是,所有消息都将Message属性设置为null,而不是当前消息。

我已经包含在我的代码下面,如果有人对如何修复绑定有任何想法,我将非常感激,因为这是我第一次使用WPF。

MessageViewModel

using System;
using System.ComponentModel;

namespace TestApp
{
    public class MessageViewModel : INotifyPropertyChanged
    {
        private string _sender;

        private string _subject;

        private string _content;

        public event PropertyChangedEventHandler PropertyChanged;

        public string Sender
        {
            get { return _sender; }
            set
            {
                _sender = value;
                NotifyPropertyChanged("Sender");
            }
        }

        public string Subject
        {
            get { return _subject; }
            set
            {
                _subject = value;
                NotifyPropertyChanged("Subject");
            }
        }

        public string Content
        {
            get { return _content; }
            set
            {
                _content = value.Trim();
                NotifyPropertyChanged("Content");
            }
        }

        private void NotifyPropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }
    }
}

MessageListViewModel

using System.Collections.ObjectModel;

namespace TestApp
{
    public class MessageListViewModel
    {
        private readonly ObservableCollection<MessageViewModel> _messages;

        public ObservableCollection<MessageViewModel> Messages
        {
            get { return _messages; }
        }

        public MessageListViewModel()
        {
            _messages = new ObservableCollection<MessageViewModel>();
        }

        #region Methods
        public void AddMessage(string sender, string subject, string content)
        {

            var vm = new MessageViewModel()
            {
                Sender = sender,
                Subject = subject,
                Content = content,
            };
            _messages.Insert(0, vm); // Insert it at the top of the list.
        }
        #endregion // Methods
    }
}

MessageUserControl

<UserControl x:Class="TestApp.MessageUserControl"
             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:TestApp"
             mc:Ignorable="d" x:Name="MessageCtrl" Background="White"
             d:DesignHeight="300" d:DesignWidth="300">
    <DockPanel>
        <Border BorderThickness="0,0,0,1" BorderBrush="DarkGray" DockPanel.Dock="Top">
            <Grid DockPanel.Dock="Top">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="60"/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>

                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>

                <TextBlock Name="lblSender" Text="Sender" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="0" FontWeight="Bold" Padding="5"/>
                <TextBlock Name="txtSender" Text="{Binding Path=Sender, Mode=TwoWay}" HorizontalAlignment="Stretch" Grid.Row="0" Grid.Column="1" Padding="5"/>

                <TextBlock Name="lblSubject" Text="Subject" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="0" FontWeight="Bold" Padding="5"/>
                <TextBlock Name="txtSubject" Text="{Binding Path=Subject, Mode=TwoWay}" HorizontalAlignment="Stretch" Grid.Row="1" Grid.Column="1" Padding="5"/>
            </Grid>
        </Border>

        <TextBox BorderThickness="0" Background="Transparent" IsReadOnly="True" Name="txtMessageContent" Text="{Binding Path=Content, Mode=TwoWay}" DockPanel.Dock="Top" Padding="5" VerticalAlignment="Center" HorizontalAlignment="Stretch" TextWrapping="WrapWithOverflow" />
    </DockPanel>
</UserControl>

MessageListUserControl

<UserControl x:Class="TestApp.MessageListUserControl"
             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:TestApp"
             mc:Ignorable="d" Background="White" x:Name="MessagesListCtrl"
             d:DesignHeight="300" d:DesignWidth="300">
    <DockPanel>
        <ItemsControl x:Name="MessagesStack" Grid.Column="0" ItemsSource="{Binding Path=Messages}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Vertical"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:MessageControl Message="{Binding Path=., Mode=TwoWay}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </DockPanel>
</UserControl>

结果最终看起来如下,主题为空,发件人和内容:

Empty message details

我还尝试将DataTemplate中的MessageListUserControl中的绑定设置为{Binding /} here但未成功,并设置DataContext={Binding}

当我将单个消息显示的模板XAML直接移动到DataTemplate时,一切正常但我希望将它分成一个单独的控件来执行一些额外的逻辑。

1 个答案:

答案 0 :(得分:0)

中删除Message属性

<local:MessageControl Message="{Binding Path=., Mode=TwoWay}" />

根本不需要。

从您现在已经完成的DataContext移除MessageControl

一切都保持不变,现在它会起作用。

DataTemplate会自动将DataContext设为MessageViewModel