在itemcontrol

时间:2015-10-03 12:12:46

标签: wpf xaml data-binding

我有2个任务。

  1. 将单个用户控件添加到父窗口。
  2. 将一个usercontrol集合添加到父窗口。
  3. 我在完成与数据绑定和命令绑定相关的任务2时遇到问题。

    如果有人知道如何执行任务2,请添加一些代码。 这是我对这两个任务的实现,以防有人想要解决它..:

    我有一个名为" Book"的用户控件。包含3个文本块和一个按钮。 userControl具有我的图书模型的dependecyProperty和按钮命令。

    Book.xaml

     <UserControl x:Name="MyBookControl"
        <Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor,  
        AncestorType={x:Type Controls:BookControl}}, Path=TheBook}">
       <Label Grid.Row="0">Title</Label>
            <Label Grid.Row="1">Author</Label>
            <Label Grid.Row="2">Description</Label>
    
            <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Title}"/>
            <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Author}"/>
            <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Description}"/>
         <Button Grid.Row="3" Command="{Binding 
        SomeCommand,ElementName=MyBookControl}" Content="Save" />
        </Grid>
    

    Book.xaml.cs

    public partial class BookControl : UserControl
        {
            public BookControl()
            {
                InitializeComponent();
            }
    
            public BookModel TheBook
            {
                get { return (BookModel)GetValue(TheBookProperty); }
                set { SetValue(TheBookProperty, value); }
            }
            public static DependencyProperty TheBookProperty = DependencyProperty.Register("TheBook", typeof(BookModel), typeof(BookControl));
    
            public ICommand SomeCommand
            {
                get { return (ICommand)GetValue(SomeCommandProperty); }
                set { SetValue(SomeCommandProperty, value); }
            }
    
            public static readonly DependencyProperty SomeCommandProperty =
                DependencyProperty.Register("SomeCommand", typeof(ICommand), typeof(BookControl), new UIPropertyMetadata(null));
        }
    

    BookModel.cs

     public class BookModel
        {
            public string Title { get; set; }
            public string Author { get; set; }
            public string Description { get; set; }
        }
    

    为了完成任务1,我创建了一个窗口: BookWindow

    <Window
           DataContext="{Binding Source={StaticResource Locator}, Path=Main}"
    >
    <StackPanel>
    <Controls:BookControl TheBook="{Binding Book}" SomeCommand="{Binding 
        SaveCommand}" />
        </StackPanel>
    

    BookViewModel.cs

        public BookModel Book { get; set; }
     public MainViewModel()
            {
                Book = new BookModel{Title = "A Book", Author = "Some Author",   
                Description = "Its a really good book!"};
            }
    
         private ActionCommand _SaveCommand;
                public ICommand SaveCommand
                {
                    get
                    {
                        if (_SaveCommand == null)
                        {
                            _SaveCommand = new ActionCommand(OnSaveCommand, CanSaveCommand);
                        }
                        return _SaveCommand;
                    }
                }
                protected virtual void OnSaveCommand()
                {
                    MessageBox.Show("save clicked");
                }
    
                protected virtual bool CanSaveCommand()
                {
                    return true;
                }
    

    很好,任务1完成 https://onedrive.live.com/redir?resid=3A8F69A0FB413FA4!116&authkey=!AHiyrfEnBr2a-rM&v=3&ithint=photo%2cpng

    现在,尝试完成任务2:

    ContainerWindow:

    <Window 
      DataContext="{Binding Source={StaticResource Locator}, Path=Container}"
            >
      <StackPanel>
            <ItemsControl ItemsSource="{Binding Books}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Vertical" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Controls:BookControl  />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
    
            </ItemsControl>
        </StackPanel>
    

    ContainerViewModel.cs:

      private ObservableCollection<BookModel> books;
            public ObservableCollection<BookModel> Books
            {
                get
                {
                    if (books == null)
                    {
                        // Not yet created.
                        // Create it.
                        books = new ObservableCollection<BookModel>();
                    }
    
                    return books;
                }
            }
    
            public ContainerViewModel()
            {
                BookModel book1 = new BookModel { Title = "A Book 2", Author = "Some Author", Description = "Its a really good book!" };
                BookModel book2 = new BookModel { Title = "A Book 3", Author = "Some Author", Description = "Its a really good book!" };
                Books.Add(book1);
                Books.Add(book2);
            }
    

    绑定失败,按钮&#34;保存&#34;停止重新编码。 https://onedrive.live.com/redir?resid=3A8F69A0FB413FA4!121&authkey=!AKnyQk6Ge_9QHug&v=3&ithint=photo%2cpng

    那么,发生了什么?为什么绑定失败,为什么按钮&#34;保存&#34;不起作用?

1 个答案:

答案 0 :(得分:1)

您未在列表示例中设置DependencyProperties。

<DataTemplate>
        <Controls:BookControl  />
</DataTemplate>

看看你是如何在非列表版本中完成的。

<Controls:BookControl TheBook="{Binding Book}" SomeCommand="{Binding 
SaveCommand}" />

话虽如此,您根本不需要DependencyProperties,UserControl将继承每个Book&#39;在ItemsControl创建它们的书籍列表中。您只需要不在网格上设置DataContext。

然后你的按钮可以绑定到BookViewModel命令属性。

<Button Grid.Row="3" Command="{Binding SaveCommand}" Content="Save" />

如果您的担忧不知道继承的DataContext可用的内容,您可以这样做以获得设计时支持。

d:DataContext="{d:DesignInstance Type=local:BookViewModel,
                                          IsDesignTimeCreatable=False}"

只需确保在文件中的某处定义了以下内容,通常是默认情况下。

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"

<强>更新

所以我错过了第二个问题,应该实际上已经启动了Visual Studio。问题是您的命令位于MainViewModel.cs中。也就是说,我们的UserControl继承了每个Book对象的DataContext。缺点是按钮正在Book对象中查找命令。

我将假设您有一个保存命令,您将编辑Book对象。因此,让我们借此机会继续制作一个ViewModel。我要将save命令移动到那里,以便保存总是可以通过BookViewModel获得。可以有充分的理由在其他地方使用save命令,但为了简单起见,我们将它放在ViewModel中。

此外,我不确定您是否在任何地方实施了INotifyPropertyChanged,因为您的MainViewModel和ContainerViewModel没有显示使用了一个。如果您不这样做,我强烈建议您退后一步,查看ViewModel的实现或MVVM框架。

BookViewModel.cs

public class BookViewModel
{
    private readonly BookModel book;

    public BookViewModel(BookModel book)
    {
        this.book = book;
        SaveCommand = new ActionCommand(OnSaveCommand, CanSaveCommand);
    }

    public ICommand SaveCommand { get; private set; }

    public string Title
    {
        get { return book.Title; }
        set { book.Title = value; }
    }

    public string Author
    {
        get { return book.Author; }
        set { book.Author = value; }
    }

    public string Description
    {
        get { return book.Description; }
        set { book.Description = value; }
    }

    protected virtual void OnSaveCommand()
    {
        MessageBox.Show("Save clicked for the book '" + Title + "'.");
    }

    protected virtual bool CanSaveCommand()
    {
        return true;
    }
}

这是你可能想要做的一个非常基本的例子。我想保持简单,不要脱离示例,你可能想要至少做一些空检查。

通过上述内容,您不必更改任何UserControl,我必须添加行和列定义,但我最终得到以下内容:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Label Grid.Row="0" Grid.Column="0">Title</Label>
    <TextBlock Grid.Row="0"
               Grid.Column="1"
               Text="{Binding Title}" />
    <Label Grid.Row="1" Grid.Column="0">Author</Label>
    <TextBlock Grid.Row="1"
               Grid.Column="1"
               Text="{Binding Author}" />
    <Label Grid.Row="2" Grid.Column="0">Description</Label>
    <TextBlock Grid.Row="2"
               Grid.Column="1"
               Text="{Binding Description}" />
    <Button Grid.Row="3"
            Grid.Column="0"
            Command="{Binding SaveCommand}"
            Content="Save" />
</Grid>

希望您注意到我们的BookViewModel构造函数接受了一本书,这意味着我们需要更改ContainerViewModel以容纳正确的集合并正确创建它们。

public class ContainerViewModel
{
    private ObservableCollection<BookViewModel> books;

    public ContainerViewModel()
    {
        Books.Add(
            new BookViewModel(new BookModel
            {
                Title = "A Book 2",
                Author = "Some Author",
                Description = "Its a really good book!"
            }));
        Books.Add(
            new BookViewModel(new BookModel
            {
                Title = "A Book 3",
                Author = "Some Author",
                Description = "Its a really good book!"
            }));
    }

    public ObservableCollection<BookViewModel> Books
    {
        get
        {
            if (books == null)
            {
                // Not yet created.
                // Create it.
                books = new ObservableCollection<BookViewModel>();
            }

            return books;
        }
    }
}

所有这些以及您的ItemsControl可以简单地如下:

<ItemsControl ItemsSource="{Binding Path=Books}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <local:MyBookControl />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>