WPF用户控件和数据绑定

时间:2010-09-08 08:31:34

标签: c# wpf xaml data-binding user-controls

我想我有点卡在WPF DataBinding的机制上。到目前为止,我已经把它想象成这样:DataBinding的任何目标必须是DependencyProperty.Keeping,记住,我设计了一个非常简单的基于按钮的UserControl,如下所示(归结为最必要的部分):

XAML

<Button x:Class="MyNamespace.IconButton"
         DataContext="{Binding RelativeSource={RelativeSource Self}}">
  <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Image x:Name="imIcon"
               Source="{Binding Path=ImageSource}" 
               Grid.Column="0" 
               Width="16" Height="16"
               Margin="0,0,5,0"/>
    <TextBlock x:Name="tbText" Text="{Binding Path=Text}" Grid.Column="1"/>
  </Grid>
</Button>

C#

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace MyNamespace
{
public partial class IconButton : Button
{
    public string Text
    {
        get { return this.tbText.Text; }
        set { this.tbText.Text = value; }
    }

    public ImageSource ImageSource
    {
        get
        {
            return (ImageSource)GetValue(ImageSourceProperty);
        }
        set
        {
            SetValue(ImageSourceProperty, value);
        }
    }   

    public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("ImageSource",typeof(ImageSource), typeof(IconButton));

    public IconButton()
    {
        InitializeComponent();
    }
  }
}

使用此控件非常简单:

<v:IconButton Margin="5,0,0,0" Height="24" Width="100"
                         Text="reload"
                         ImageSource="..\Resources\images\refresh.png"/>

Visual Studio Designer显示按钮和文本,但拒绝评估依赖项属性“ImageSourceProperty”。当我将'Text'实现为DependencyProperty时也会发生同样的情况。为什么这样做?

而且,为什么我需要一个常规的'ImageSource'呢?如果我从我的代码中删除此属性,VS XAML编辑器(使用控件)将使用“System.Windows.DependencyProperty MyNamespace.IconButton.ImageSourceProperty”(而不是常规属性描述)Tooltip ImageSource属性,这意味着它可以正确解析依赖属性,然而我不能以这种方式设置任何值(“附加属性没有设置器”)。为什么我只能通过xaml的常规属性设置dependecy属性的值?

最后,我有另一个问题。为了简化说明,我在此示例中使用了Article-Category设置。 UI应该允许用户重新分类商店文章。

<ListView x:Name="lvArticles"
          ItemsSource="{Binding Path=Articles}"
          IsSynchronizedWithCurrentItem="True"/>

<ComboBox IsSynchronizedWithCurrentItem="True"
          x:Name="cbCategories"
          ItemsSource="{Binding Path=Categories}" 
          SelectedItem="{Binding Path=Articles.CurrentItem.Category, Mode=TwoWay}"/>

我可以通过在cbCategories中进行选择来改变我的文章(即设置新类别),但是当从lvArticles中选择文章时,cbCategories不会自动设置为相关的类别。我正在做什么或者这是另一个WPF异常现象有什么问题吗?

提前感谢任何提示...

修改

从昨天开始,除了这种奇怪的ComboBox行为之外,我还没有考虑过任何事情。我在代码中发现的第一个缺陷是ViewModel中进入ComboBox的Equals方法缺少覆盖。结果,没有任何机会成为SelectedItem - 所有ItemSource的元素与SelectedItem的比较都导致了一个整洁的'False'。我现在修补了这个。但在我的Master-Detail-View中,更改所选主记录时,详细信息仍然不会捕捉到正确的值。

另一个编辑

为了弥补这一点,我创造了一个非常丑陋的事件驱动的解决方法。

<ListView x:Name="lvArticles"
          ItemsSource="{Binding Path=Articles}"
          IsSynchronizedWithCurrentItem="True"
          SelectionChanged="LvArticlesSelectionChanged"/>

底层事件处理程序如下所示:

private void LvArticlesSelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {
        ArticleViewModel avm = this.Articles.SelectedItem as ArticleViewModel;
        foreach (CategoryViewModel cvm in this.ViewModel.Categories)
        {
            if( cvm.Equals( avm.Category ) )
            {
                this.cbCategories.SelectedItem = cvm;
            }
        }
    }

这是我能想象到的最难看的解决方案 - 我们有什么绑定?但是,由于我必须提供,我会继续这样做,直到有人得到一个很好的解释......

1 个答案:

答案 0 :(得分:0)

您将DataSource绑定到Button,而不是绑定到IconButton:

DataContext="{Binding RelativeSource={RelativeSource Self}}"

我的猜测是你希望绑定到原来的父母身上?

两个选项:

选项1 [更改RelativeSource绑定]:

最简单的解决方案:

更改DataSource事物以找到正确的父级。

DataContext="{Binding RelativeSource={RelativeSource FindAncestor, 
                     AncestorType={x:Type MyNamespace:IconButton}}}"

选项2 [模板绑定]:

重写您的控件,以便它使用ControlTemplate,因此您可以对要从父控件接管的属性使用TemplateBindings

<Button x:Class="MyNamespace.IconButton">
  <Button.Template>
      <ControlTemplate TargetType="MyNamespace:IconButton">
          <Grid>
        ...
        <Image x:Name="imIcon"
               Source="{TemplateBinding ImageSource}" 
               Grid.Column="0" 
               Width="16" Height="16"
               Margin="0,0,5,0"/>
        <TextBlock x:Name="tbText" Text="{TemplateBinding Text}" Grid.Column="1"/>
          </Grid>
      </ControlTemplate>
  <Button.Template>
</Button>