WPF中的自定义UserControl /列表视图控件

man*_*der 3 wpf xaml user-controls

我正在开发一个自定义控件。我的自定义控件中有一个列表视图,该列表视图假定显示来自绑定EF实体的多个字段。输出将是这样的

在此处输入图片说明

现在,列表视图的数据源是什么,因为我的所有实体都有不同的属性,所以我不确定绑定属性。

现在我的ListView有一个图像控件,两个文本块和一个链接标签,我应该如何确定应该绑定哪个属性的控件。>

我喜欢在RecordListing屏幕中使用此控件,例如说“ Client屏幕绑定到Client实体”和“ Employee屏幕绑定到Employee实体”。我的控件中一次应该有一个实体,

请指导我如何做到这一点,这是一种非常普通和合乎逻辑的方法。

谢谢

Jas*_*dge 5

就像Foovanadil建议的那样,我将公开一个DataSource DependencyProperty。但除此之外,我还将介绍5个更多的字符串依赖项属性。然后,控件的使用者会将他们想要使用的属性的名称放入特定的控件中。

让我更具体一点:

想一想Combobox的工作方式,可以在其中绑定其DataSource,但也可以提供DisplayMemberPathSelectedValuePath来指定要使用的数据源中的属性。

您可以使用控件执行相同的操作:

公开“ ImagePathMember”属性。这将是属性的名称,该属性包含图像控件中的图像路径

公开“ LinkPathMember”属性。此属性将是包含链接路径的属性的名称

公开“ LinkDisplayMember”属性。此属性将是属性名称,该属性包含链接看起来像的文本。

公开“ TopTextBlockMember”属性。此属性将是包含顶部文本块文本的属性的名称

公开“ BottomTextBlockMember”属性。该属性将是包含底部文本块文本的属性的名称

然后,您只需在控件中使用反射来确定每个列表框项目的值,然后将其绑定到listboxitem的图像控件,链接和2个文本块

希望能有所帮助

u_u


编辑

好的,您要求输入一些代码以指示正确的方向。

首先,依赖属性

public static DependencyProperty ImagePathMemberProperty = DependencyProperty.Register("ImagePathMember", typeof(string), typeof(MyCustomControl), new PropertyMetadata("",ImagePathMemberPropertyChanged));

public static DependencyProperty DataSourceProperty = DependencyProperty.Register("DataSource", typeof(string), typeof(MyCustomControl), new PropertyMetadata("",DataSourceChanged));
public string ImagePathMember
{
    get
    {
        return (string)GetValue(ImagePathMemberProperty);
    }
    set
    {
        SetValue(ImagePathMemberProperty, value);
    }
}
public string DataSource
{
    get
    {
        return (string)GetValue(DataSourceProperty);
    }
    set
    {
        SetValue(DataSourceProperty, value);
    }
}
Run Code Online (Sandbox Code Playgroud)

背后的代码

事实证明,我们甚至不需要反思。当在代码中使用绑定时,实际上为该属性提供了一个字符串名称,这很方便,因为我们创建的依赖项属性实际上是该属性的字符串名称。幸运的我们!

private void DataSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    RecreateBindings();
}

//Repeat this for all the dependencyproperties
private void ImagePathMemberPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    RecreateBindings();

}

private void RecreateBindings()
{
      //Repeat this for all dependency properties
      if(ImagePathMember!=null)
      {
           Binding ImagePathBinding= new Binding(ImagePathMember);
           ImagePathBinding.UpdateSourceTrigger =  UpdateSourceTrigger.PropertyChanged;

           MyImageControl.SetBinding(ImageControl.ImagePathProperty, ImagePathBinding);

      }
}
Run Code Online (Sandbox Code Playgroud)

我手动编写了此代码,因此可能都是越野车。另外,我对绑定有一点担心,我没有设置a,Source因为我认为它会绑定到集合中的项目上,但是我不确定这种行为。

因此,我现在将其发布,然后进行测试运行,并在需要时进行调整。

祝好运

u_u


编辑2

好吧,结果比我想象的要复杂得多。当TextBlocks位于DataTemplate中时,仅通过在后面的代码中调用它们的名称就不能真正访问它们。

您要做的是等待ListBox / ListView生成其项目的容器,然后使用visualtreehelper遍历listview的所有子级以查找您要查找的特定控件,然后将其绑定到它们。

这花了我很长时间,因为我找不到控件,因为我在ListView的ItemsSourceChanged事件上附加了一个事件处理程序,这意味着我看起来是ItemsSource属性发生了变化,但是生成这些项目的容器之前

最终我找到了解决方案:

XAML:

在具有控件的ListView / ListBox的模板中,您需要这样命名它们:

      <ImageControl x:Name="MyImageControl" [...]></ImageControl>
Run Code Online (Sandbox Code Playgroud)

您还需要给列表框/列表视图命名,就像这样(并将其ItemsSource绑定到您的DataSource属性):

      <ListBox x:Name="listbox"  ItemsSource="{Binding ElementName=me, Path=DataSource, UpdateSourceTrigger=PropertyChanged}" [...]></ListBox> 
Run Code Online (Sandbox Code Playgroud)

您将看到绑定具有ElementName=me。这是因为我绑定到我所在的实际控件(即MyCustomControl)。我UserControlx:Name="me"上面的xmlns,因为这很容易让我绑定到后面的代码性能。

RecreateBindings:

基本上,您需要改进RecreateBindings方法。我在第一篇文章中犯了一个大错误,因为它必须是静态方法才能在DependencyProperty的PropertyChangedCallBack中运行(我真的不应该手工编写代码)。

我最终得到的是:

 //Repeat this for all types of controls in your listbox.
 private static void RecreateImageControlBindings(ListBox listbox, string controlName, string newPropertyName)
    {

        if (!string.IsNullOrEmpty(newPropertyName))
        {
            if (listbox.Items.Count > 0)
            {
                for (int i = 0; i < listbox.Items.Count; i++)
                {

                    ListBoxItem item = listbox.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
                    if (item != null)
                    {
                        Binding imageControlBinding = new Binding(newPropertyName);
                        imageControlBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

                        ImageControl t = FindDescendant<ImageControl>(item, controlName);
                        if (t != null)
                            BindingOperations.SetBinding(t, ImageControl.ImagePath, imageControlBinding);


                    }
                }

            }



        }

    }
Run Code Online (Sandbox Code Playgroud)

如您所见,现在您需要一个recreateBindings方法用于listview / listbox中的所有不同类型的控件。有很多更通用的方法可以做到,但是您可以自己解决。我不能做所有的工作:P

代码正在做的是遍历列表框中的项目并获取其容器。ImageControl一旦生成,将成为该容器的子级。所以,我们去通过FindDescendants方法我改编自的帮助下得到了ImageControl 这一职位

这是该方法:

FindDescendant方法

public static T FindDescendant<T>(DependencyObject obj,string objectName) where T : FrameworkElement
    {

            // Check if this object is the specified type
            if (obj is T && ((T)obj).Name == objectName)
                return obj as T;

            // Check for children
            int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
            if (childrenCount < 1)
                return null;

            // First check all the children
            for (int i = 0; i < childrenCount; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                if (child is T && ((T)child).Name == objectName)
                    return child as T;
            }

            // Then check the childrens children
            for (int i = 0; i < childrenCount; i++)
            {
                DependencyObject child = FindDescendant<T>(VisualTreeHelper.GetChild(obj, i), objectName);
                if (child != null && child is T && ((T)child).Name == objectName)
                    return child as T;
            }

            return null;


    }
Run Code Online (Sandbox Code Playgroud)

我所做的唯一修改就是添加了控件名称的检查。我们的ListBoxItem中有2个TextBlocks,因此原始方法将仅返回第一个。我们需要检查名称,以便可以对两者进行绑定。

PropertyCallBack方法:

因此,由于拆分了RecreateBindings方法,因此我们需要更改PropertyChangedCallBacks以调用特定于每个属性的RecreateBindings方法。数据源属性将具有所有RecreateBindings方法。

private static void DataSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {

       //Put the RecreateBindings for all the properties here:
        RecreateImageControlBindings(((MyCustomControl)sender).listbox, "MyImageControl", ((MyCustomControl)sender).ImagePathMember);

    }

    //Repeat this for all the dependencyproperties
    private void ImagePathMemberPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
         RecreateImageControlBindings(((MyCustomControl)sender).listbox, "MyImageControl", ((MyCustomControl)sender).ImagePathMember);

    }
Run Code Online (Sandbox Code Playgroud)

请注意,MyCustomControl是您要创建的此控件的类型。

构造函数和附加的事件处理程序:

最后,我们需要向构造函数添加一行,从而向ListBox的ItemContainerGenerator添加事件处理程序,以便我们可以检查何时生成项目容器,并可以附加绑定。

    public MyCustomControl()
    {

        InitializeComponent();     

        listview.ItemContainerGenerator.StatusChanged += new EventHandler(ItemContainerGenerator_StatusChanged);

    }

    void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
    {
        if (listview.ItemContainerGenerator.Status
        == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
        {
            //Do this for all the different bindings we want
            RecreateImageControlBindings(listview, "MyImageControl", ImagePathMember);

        }
    }
Run Code Online (Sandbox Code Playgroud)

应该是这样。如果您需要任何帮助/有任何疑问,请通知我

u_u