WPF 自定义控件 - 如何从静态 ObservableCollection 绑定列表框?

发布于 2024-09-07 12:35:46 字数 2882 浏览 6 评论 0原文

我不知道具体该怎么做。我在 NameField.cs 类中有一个静态 ObservableCollection。我只是不知道如何将它绑定到列表框。

  • 我不应该使用列表框吗?
  • 我应该使用 DependencyProperty 吗?
  • 我应该通过财产还是公共来公开 ObservableCollection?

我不知道在这里做什么......

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace MyWPF
{

    [TemplatePart(Name = NameField.ElementPrefixBox, Type = typeof(ListBox))]
    public class NameField : Control
    {
        private const String ElementPrefixBox        = "PART_PrefixBox";

        private static ObservableCollection<NamePrefix> _namePrefixes;

        static NameField()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(NameField), new FrameworkPropertyMetadata(typeof(NameField)));

            _namePrefixes = new ObservableCollection<NamePrefix>();
        }

        public static void AddNamePrefix(NamePrefix namePrefix)
        {
            lock (_namePrefixes)
            {
                _namePrefixes.Add(namePrefix);
            }
        }

        public static IEnumerator<NamePrefix> GetNamePrefixes()
        {
            return _namePrefixes.GetEnumerator();
        }

    }

    /// <summary>
    /// A Key/Value structure containing a Name Prefix ID and String value.
    /// </summary>
    public struct NamePrefix
    {
        public NamePrefix(Int32 id, String prefix)
            : this()
        {
            ID = id;
            Prefix = prefix;
        }

        public Int32 ID { get; set; }
        public String Prefix { get; set; }
    }

}

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyWPF"
    xmlns:con="clr-namespace:MyWPF.Converters"
    >

    <Style TargetType="{x:Type local:NameField}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:NameField}">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock TextWrapping="NoWrap" Text="Name:" VerticalAlignment="Center" Margin="3" />
                        <ListBox x:Name="PART_PrefixBox" VerticalAlignment="Center" Margin="3" >
                            <ListBox.ItemBindingGroup>
                                <BindingGroup Name="NamePrefixes"/>
                            </ListBox.ItemBindingGroup>
                        </ListBox>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

I'm not sure how to go about this exactly. I have a static ObservableCollection in the NameField.cs class. I just have no idea how to bind it to the listbox.

  • Should I not be using a ListBox?
  • Should I be using a DependencyProperty?
  • Should I be exposing the ObservableCollection through a property or by public?

I'm not sure what to do here...

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace MyWPF
{

    [TemplatePart(Name = NameField.ElementPrefixBox, Type = typeof(ListBox))]
    public class NameField : Control
    {
        private const String ElementPrefixBox        = "PART_PrefixBox";

        private static ObservableCollection<NamePrefix> _namePrefixes;

        static NameField()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(NameField), new FrameworkPropertyMetadata(typeof(NameField)));

            _namePrefixes = new ObservableCollection<NamePrefix>();
        }

        public static void AddNamePrefix(NamePrefix namePrefix)
        {
            lock (_namePrefixes)
            {
                _namePrefixes.Add(namePrefix);
            }
        }

        public static IEnumerator<NamePrefix> GetNamePrefixes()
        {
            return _namePrefixes.GetEnumerator();
        }

    }

    /// <summary>
    /// A Key/Value structure containing a Name Prefix ID and String value.
    /// </summary>
    public struct NamePrefix
    {
        public NamePrefix(Int32 id, String prefix)
            : this()
        {
            ID = id;
            Prefix = prefix;
        }

        public Int32 ID { get; set; }
        public String Prefix { get; set; }
    }

}

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyWPF"
    xmlns:con="clr-namespace:MyWPF.Converters"
    >

    <Style TargetType="{x:Type local:NameField}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:NameField}">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock TextWrapping="NoWrap" Text="Name:" VerticalAlignment="Center" Margin="3" />
                        <ListBox x:Name="PART_PrefixBox" VerticalAlignment="Center" Margin="3" >
                            <ListBox.ItemBindingGroup>
                                <BindingGroup Name="NamePrefixes"/>
                            </ListBox.ItemBindingGroup>
                        </ListBox>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

紫瑟鸿黎 2024-09-14 12:35:46

现在,我不确定你在做什么,因为你的类扩展了 Control,所以我假设你正在创建一个自定义控件,所以我的答案是基于此。我还假设 NamePrefixes 永远不会改变,这就是您使用静态名称的原因。

我会跳过静态并执行此操作:

public class NameField : Control
{
    private const String ElementPrefixBox        = "PART_PrefixBox";

    public ObservableCollection<NamePrefix> NamePrefixes {get;private set;}
    public NameField()
    {
        NamePrefixes = new ObservableCollection<NamePrefix>();
    }
}

<Style TargetType="{x:Type local:NameField}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:NameField}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock TextWrapping="NoWrap" Text="Name:" VerticalAlignment="Center" Margin="3" />
                    <ListBox x:Name="PART_PrefixBox" 
                        VerticalAlignment="Center" 
                        Margin="3" 
                        ItemsSource="{Binding NamesPrefix, RelativeSource={RelativeSource FindAncestor, Ancestortype=Whatever}}" />
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

将 ItemsSource 绑定到自定义控件的根(无法从代码中看出它是什么)。您也许可以为您的根应用一个名称,然后只需使用 ElementName=,IIRC 它有时会起作用。

如果您绝对需要将其设为静态,因为所有控件在任何一个控件更新时都必须更新,那么您可以将可观察集合设为静态并将 ItemsSource 绑定到 {x:Static local:NameField.NamesPrefix}。只需意识到您只能绑定到公共属性,而不能绑定到字段或方法(不使用对象数据源或其他东西)。

Now, I'm not sure what you're doing as your class extends Control, so I'm assuming you're creating a custom control, so my answer is based off that. I'm also assuming that NamePrefixes never changes, which is why you're using a static for it.

I'd skip the statics and do this:

public class NameField : Control
{
    private const String ElementPrefixBox        = "PART_PrefixBox";

    public ObservableCollection<NamePrefix> NamePrefixes {get;private set;}
    public NameField()
    {
        NamePrefixes = new ObservableCollection<NamePrefix>();
    }
}

<Style TargetType="{x:Type local:NameField}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:NameField}">
                <StackPanel Orientation="Horizontal">
                    <TextBlock TextWrapping="NoWrap" Text="Name:" VerticalAlignment="Center" Margin="3" />
                    <ListBox x:Name="PART_PrefixBox" 
                        VerticalAlignment="Center" 
                        Margin="3" 
                        ItemsSource="{Binding NamesPrefix, RelativeSource={RelativeSource FindAncestor, Ancestortype=Whatever}}" />
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

You bind the ItemsSource to the root of the custom control (can't tell from your code what it is). You might be able to apply a name to your root and then just use ElementName=, IIRC it works sometimes.

If you ABSOLUTELY need to make it static because ALL controls must be updated when any one of them is updated, then you can make the observable collection static and bind ItemsSource to {x:Static local:NameField.NamesPrefix}. Just realize you can only bind to public properties, not to fields or methods (without using an object data source or something).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文