Wpf 绑定到usercontrol中的依赖项属性

Wpf 绑定到usercontrol中的依赖项属性,wpf,binding,user-controls,dependency-properties,Wpf,Binding,User Controls,Dependency Properties,我有一个包含ListBox的UserControl,我想跟踪该ListBox的SelectedItems。 UserControl有一个DP“SelectedItemsList”,定义如下 public static DependencyProperty SelectedItemsListProperty = DependencyProperty.Register( "SelectedItemsList", typeof (IList), typeof (MyListControl)

我有一个包含ListBox的UserControl,我想跟踪该ListBox的SelectedItems。 UserControl有一个DP“SelectedItemsList”,定义如下

public static DependencyProperty SelectedItemsListProperty = DependencyProperty.Register(
  "SelectedItemsList",
  typeof (IList),
  typeof (MyListControl),
  new FrameworkPropertyMetadata(null, 
    OnSelectedItemsChanged));
在listbox“Item“SelectionChanged”事件中,我想将所选项目保存到DP。每当我更改列表框中的选择时,就会触发此操作

private void OnItemSelectionChanged(object sender, SelectionChangedEventArgs e)
{
  SelectedItemsList = this.myListBox.SelectedItems;
}
在包含“MyListControl”的视图中,我创建了一个要使用选定项的viewmodel绑定

 <controls:MyListControl 
  Source="{Binding SomeItemsList, UpdateSourceTrigger=PropertyChanged}"
  SelectedItemsList="{Binding SelectedItems, UpdateSourceTrigger=PropertyChanged}"/>
我对“new ArrayList”部分还不满意,但如果我想在viewmodel的属性设置器中检查是否相等,则SelectedItemsList不能作为SelectedItems的引用。上一个值和新值将始终相同

然后,我将UserControl“MyListControl”中的项目选择部分简化为依赖项属性本身:

public static DependencyProperty SelectedItemsProperty =  DependencyProperty.Register(
  "SelectedItems",
  typeof (IList),
  typeof (MyListControl),
  new FrameworkPropertyMetadata(null));


public IList SelectedItems
{
  get
  {
    return (IList)GetValue(SelectedItemsProperty);
  }
  set
  {
    SetValue(SelectedItemsProperty, value);
  }
}
并修改了MyListControl的xaml:

  <controls:CustomListBox  
       SelectionMode="Extended" 
       ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:MyListControl}},
       Path=Source, UpdateSourceTrigger=PropertyChanged}"           
       SelectedItemsList="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:MyListControl}},
       Path=SelectedItems, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
       >
此属性的类型必须为IList,否则setter中的值将始终为null

在视图的xaml中

<controls:MyListControl
  Source="{Binding CurrentImageList, UpdateSourceTrigger=PropertyChanged}"
  SelectedItems="{Binding SelectedObjects, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
 />

我今天遇到了同样的问题,不幸的是,当您分配给SelectedItemsList一个值时,WPF似乎将其解除绑定。为了修复它,我更新了绑定项中的值。我知道这不是世界上最好的解决方案,但对我来说,它是有效的。 在本例中,代码如下所示:

private void OnItemSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    this.SetPropertyValue(
    this.GetBindingExpression(SelectedItemsListProperty), 
                this.myListBox.SelectedItems);
}


    private void SetPropertyValue(BindingExpression bindingExpression, object value)
    {
        string path = bindingExpression.ParentBinding.Path.Path;

        var properties = new Queue<string>(
            path.Split(
                new[]
                    {
                        '.'
                    }).ToList());

        this.SetPropertyValue(bindingExpression.DataItem, bindingExpression.DataItem.GetType(), properties, value);
    }



    private void SetPropertyValue(object destination, Type type, Queue<string> properties, object value)
    {
        PropertyInfo property = type.GetProperty(properties.Dequeue());

        if (property != null && destination != null)
        {
            if (properties.Count > 0)
            {
                this.SetPropertyValue(property.GetValue(destination), property.PropertyType, properties, value);
            }
            else
            {
                property.SetValue(destination, value);
            }
        }
    }
private void OnItemSelectionChanged(对象发送方,SelectionChangedEventArgs e)
{
this.SetPropertyValue(
此.GetBindingExpression(SelectedItemsListProperty),
这个.myListBox.SelectedItems);
}
私有void SetPropertyValue(BindingExpression、BindingExpression、对象值)
{
字符串路径=bindingExpression.ParentBinding.path.path;
var属性=新队列(
路径,分开(
新[]
{
'.'
}).ToList());
this.SetPropertyValue(bindingExpression.DataItem,bindingExpression.DataItem.GetType(),属性,值);
}
私有void SetPropertyValue(对象目标、类型、队列属性、对象值)
{
PropertyInfo property=type.GetProperty(properties.Dequeue());
if(属性!=null&&destination!=null)
{
如果(properties.Count>0)
{
this.SetPropertyValue(property.GetValue(目标)、property.PropertyType、properties、value);
}
其他的
{
SetValue(目的地,值);
}
}
}

您需要将列表框的SelectedItems绑定到DP SelectedItems列表,以将用户选择传播到DP。然后,您已经拥有的绑定将把更改传递给viewmodel,但我认为您需要一个绑定模式“双向”,而不是UpdateSourceTrigger


不要在DP中使用PropertyChangeCallback:如果SelectedItemsListProperty已更改,则更改SelectedItemsList毫无意义。(通常前者是后者的包装属性。)

在“OnItemSelectChanged”中的常规集之前添加“SelectedItemsList=null”时,可能会触发propertychangecallabck(对于两个集),但与我的ViewModel的绑定始终包含“null”.I仅使用PropertyChangeCallback来设置断点的位置,以查看是否发生任何更改。listbox没有SelectedItems属性的公共setter。因此,我无法创建对它的直接绑定。
<controls:MyListControl
  Source="{Binding CurrentImageList, UpdateSourceTrigger=PropertyChanged}"
  SelectedItems="{Binding SelectedObjects, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
 />
private void OnItemSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    this.SetPropertyValue(
    this.GetBindingExpression(SelectedItemsListProperty), 
                this.myListBox.SelectedItems);
}


    private void SetPropertyValue(BindingExpression bindingExpression, object value)
    {
        string path = bindingExpression.ParentBinding.Path.Path;

        var properties = new Queue<string>(
            path.Split(
                new[]
                    {
                        '.'
                    }).ToList());

        this.SetPropertyValue(bindingExpression.DataItem, bindingExpression.DataItem.GetType(), properties, value);
    }



    private void SetPropertyValue(object destination, Type type, Queue<string> properties, object value)
    {
        PropertyInfo property = type.GetProperty(properties.Dequeue());

        if (property != null && destination != null)
        {
            if (properties.Count > 0)
            {
                this.SetPropertyValue(property.GetValue(destination), property.PropertyType, properties, value);
            }
            else
            {
                property.SetValue(destination, value);
            }
        }
    }