【问题标题】:Custom ItemsControl and selection support自定义项目控制和选择支持
【发布时间】:2012-09-10 04:37:56
【问题描述】:

我想创建一个控件:

  • 派生自 ItemsControl,
  • 可以绑定到枚举列表,
  • 对于每个枚举,它将显示 RadioButton,
  • 选择给出放射线量时,SelectedItem将包含与RadioButton关联的枚举值。

我已经成功实现了上面列表中的前三点,但第四项有问题。我怀疑它可能与项目容器类的错误实现或 generic.xaml 中的错误项目模板定义有关。

物品容器如下所示:

public class MyEnumSelectorItem : ContentControl
{
public static readonly DependencyProperty IsSelectedProperty;

static MyEnumSelectorItem()
{
    IsSelectedProperty = Selector.IsSelectedProperty.AddOwner(typeof(MyEnumSelectorItem));
}

public bool IsSelected
{
    get { return (bool)GetValue(IsSelectedProperty); }
    set { SetValue(IsSelectedProperty, value); }
}

static readonly DependencyProperty ModeProperty = 
    DependencyProperty.Register("Mode", typeof(MyEnum), typeof(MyEnumSelector), new PropertyMetadata());
public MyEnum Mode
{
    get { return (MyEnum)GetValue(ModeProperty); }
    set { SetValue(ModeProperty, value); }
}
}

使用IsItemItsOwnContainerOverride/GetContainerForItemOverride/PrepareContainerForItemOverride 方法覆盖将项容器类型与选择器控件(派生自System.Windows.Controls.Primitives.Selector)相关联。

generic.xaml 的相关片段如下所示:

<Style TargetType="{x:Type controls:MyEnumSelector}">
    <Setter Property="ItemTemplate">
        <Setter.Value>
            <DataTemplate>
                <RadioButton Content="{Binding}" 
                     IsChecked="{Binding IsSelected, Mode=TwoWay}" 
                     GroupName="enumSelector" Height="25" FontWeight="Bold"  />
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

以上假设 IsSelected 属性的正确绑定将自动导致 SelectedItem 属性被设置(我希望这是正确的,不是吗?)。

【问题讨论】:

  • 你不应该从Selector派生吗? SelectedItem 是在 Selector 而不是 ItemsControl 上定义的。
  • 实际上它源自 Selector(正如我的问题后面所指出的那样)-在第一句话中,我的意思是它至少应该源自 ItemsControl。

标签: c# .net wpf xaml


【解决方案1】:

在我看来,仅仅调用Selector.IsSelectedProperty.AddOwner 是不够的。

查看ILSpyListBoxItem 的代码,您可以看到可能正在执行其他几项操作以在选定项和选择器本身之间进行同步,例如引发 Selected/Unselected 事件。

static ListBoxItem()
{
    ListBoxItem.IsSelectedProperty = Selector.IsSelectedProperty.AddOwner(typeof(ListBoxItem), new FrameworkPropertyMetadata(BooleanBoxes.FalseBox, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, new PropertyChangedCallback(ListBoxItem.OnIsSelectedChanged)));
    ListBoxItem.SelectedEvent = Selector.SelectedEvent.AddOwner(typeof(ListBoxItem));
    ListBoxItem.UnselectedEvent = Selector.UnselectedEvent.AddOwner(typeof(ListBoxItem));
    ...
}

private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ListBoxItem listBoxItem = d as ListBoxItem;
    bool flag = (bool)e.NewValue;
    Selector parentSelector = listBoxItem.ParentSelector;
    if (parentSelector != null)
    {
        parentSelector.RaiseIsSelectedChangedAutomationEvent(listBoxItem, flag);
    }
    if (flag)
    {
        listBoxItem.OnSelected(new RoutedEventArgs(Selector.SelectedEvent, listBoxItem));
    }
    else
    {
        listBoxItem.OnUnselected(new RoutedEventArgs(Selector.UnselectedEvent, listBoxItem));
    }
    listBoxItem.UpdateVisualState();
}

我自己还没有尝试过,看看这是否真的解决了问题,但我认为这是一个很好的起点。您还应该考虑让您的项目派生自 ListBoxItem 而不是 ContentControl

【讨论】:

  • 刚才我尝试从 ListBoxItem 派生 MyEnumSelectorItem 并(作为单独的尝试)添加 OnIsSelectedChanged 回调 - 不幸的是没有变化(并且没有调用回调)。
  • @TomaszGrobelny 如果未调用回调(并且您记得将其添加到属性的元数据中),那么您的绑定很可能有问题。您应该查找绑定错误并确保实际设置了该值。
  • 是的,它被添加到属性元数据中,就像上面一样。我现在才在 VS 的输出窗口中发现以下错误:System.Windows.Data 错误:39:BindingExpression 路径错误:在 'object' ''MyEnum' (HashCode=4)' 上找不到 'IsSelected' 属性。绑定表达式:路径=IsSelected; DataItem='MyEnum' (HashCode=4);目标元素是'RadioButton'(名称='');目标属性是'IsChecked'(类型'Nullable`1')似乎我的绑定是指原始枚举而不是容器项(如调试器所示创建)......但是我该如何编写正确的绑定?
  • @TomaszGrobelny 尝试使用TemplateBinding 而不是Binding
  • 你的意思是这样的:IsChecked="{TemplateBinding IsSelected}"?这会在编译期间导致“错误 MC3011:在类型 'ContentPresenter' 上找不到静态成员 'IsSelectedProperty'”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多