具有依赖属性的 UserControl 可替换一组重复的类似控件

发布于 2024-11-26 16:51:39 字数 1478 浏览 0 评论 0原文

抱歉,问题标题太糟糕了,我不知道如何表达,请随意发表评论。

我正在使用 MVVM 模式开发 WPF 桌面应用程序。我的 ViewModel 上有大量文本字段,我想使用下面的模式显示所有这些文本字段(大大简化):

<StackPanel Orientation="Vertical">
    <StackPanel.Style>
        <Style TargetType="{x:Type StackPanel}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=SomePredicate}" Value="False">
                    <Setter Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </StackPanel.Style>

    <Label Content="SomeHeader:"/>

    <TextBlock Text="{Binding Path=SomeText}" />

</StackPanel>

显示始终包含:

  • 用于确定是否应显示 StackPanel 的谓词
  • 用作标题文本的字符串。这可以在 XAML 中设置,它不必来自视图模型
  • 绑定到 ViewModel 上的 Textblock 文本字符串

我真的很希望能够像这样定义这些:

<MyHeaderedTextBlockControl Text="{Binding Path=SomeText}"
                            Header="SomeHeader:"
                            Predicate="{Binding SomePredicate}"/>

可以这样做吗?我尝试用 UserControl 来做这件事,但不知道我在做什么。

对我来说重要的是,绑定模式保持工作状态,即如果 Text 绑定处于 OneWayTwoWay 模式,则 TextBlock当 ViewModel 上的 Text 属性引发 OnPropertyChanged 时, 应该更新。

我也不喜欢为每个这样的文本属性使用 View 和 ViewModel 来执行此操作,因为这样我必须创建这些 ViewModel 并将它们连接起来以进行更新等。我想要一个可以在视图中实现的解决方案,理想情况下ViewModel 甚至不应该知道它。

Sorry for the terrible Question Title, I don't know how to phrase this, feel free to comment.

I am working on a WPF desktop app using the MVVM pattern. I have a great number of text fields on my ViewModel, all of which I want to display using the pattern below (greatly simplified):

<StackPanel Orientation="Vertical">
    <StackPanel.Style>
        <Style TargetType="{x:Type StackPanel}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=SomePredicate}" Value="False">
                    <Setter Property="Visibility" Value="Collapsed"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </StackPanel.Style>

    <Label Content="SomeHeader:"/>

    <TextBlock Text="{Binding Path=SomeText}" />

</StackPanel>

The display always consists of:

  • A Predicate for determining if the StackPanel should be shown
  • A string used as header text. This can be set in XAML, it doesn't have to come from the viewmodel
  • A binding to the Textblock text string on the ViewModel

I would really love to be able to define these like this:

<MyHeaderedTextBlockControl Text="{Binding Path=SomeText}"
                            Header="SomeHeader:"
                            Predicate="{Binding SomePredicate}"/>

Can this be done? I tried doing it with a UserControl but had no idea what I was doing.

It is important to me that the binding modes remain working, ie if the Text binding is in OneWay or TwoWay mode, the TextBlock should update when the Text property on the ViewModel raises OnPropertyChanged.

I also dont like the idea of doing this with a View and a ViewModel for each such text property, because then I have to create those ViewModels and wire them up for updates etc. I want a solution that I can implement in the View, ideally the ViewModel shouldn't even know about it.

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

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

发布评论

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

评论(1

奈何桥上唱咆哮 2024-12-03 16:51:39

使用 UserControls,您可以在代码中定义依赖属性,并通过绑定将它们转发到“模板”,例如:

<UserControl x:Class="Test.UserControls.HeaderedTextBlock"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Name="control">
    <StackPanel Orientation="Vertical">
        <StackPanel.Style>
            <Style TargetType="{x:Type StackPanel}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Predicate, ElementName=control}" Value="False">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </StackPanel.Style>
        <Label Content="{Binding Header, ElementName=control}" />
        <TextBlock Text="{Binding Text, ElementName=control}" />
    </StackPanel>
</UserControl>
namespace Test.UserControls
{
    public partial class HeaderedTextBlock : UserControl
    {

        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(string), typeof(HeaderedTextBlock), new UIPropertyMetadata(null));
        public string Header
        {
            get { return (string)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        public static readonly DependencyProperty PredicateProperty =
            DependencyProperty.Register("Predicate", typeof(bool), typeof(HeaderedTextBlock), new UIPropertyMetadata(false));
        public bool Predicate
        {
            get { return (bool)GetValue(PredicateProperty); }
            set { SetValue(PredicateProperty, value); }
        }

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(HeaderedTextBlock), new UIPropertyMetadata(null));
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        public HeaderedTextBlock()
        {
            InitializeComponent();
        }
    }
}

这样的东西适合您吗?

With UserControls you define dependency properties in your code and forward them to the "template" via binding, e.g.:

<UserControl x:Class="Test.UserControls.HeaderedTextBlock"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Name="control">
    <StackPanel Orientation="Vertical">
        <StackPanel.Style>
            <Style TargetType="{x:Type StackPanel}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Predicate, ElementName=control}" Value="False">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </StackPanel.Style>
        <Label Content="{Binding Header, ElementName=control}" />
        <TextBlock Text="{Binding Text, ElementName=control}" />
    </StackPanel>
</UserControl>
namespace Test.UserControls
{
    public partial class HeaderedTextBlock : UserControl
    {

        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(string), typeof(HeaderedTextBlock), new UIPropertyMetadata(null));
        public string Header
        {
            get { return (string)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        public static readonly DependencyProperty PredicateProperty =
            DependencyProperty.Register("Predicate", typeof(bool), typeof(HeaderedTextBlock), new UIPropertyMetadata(false));
        public bool Predicate
        {
            get { return (bool)GetValue(PredicateProperty); }
            set { SetValue(PredicateProperty, value); }
        }

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(HeaderedTextBlock), new UIPropertyMetadata(null));
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        public HeaderedTextBlock()
        {
            InitializeComponent();
        }
    }
}

Would something like this work for you?

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