如何将自定义 UserControl 的依赖属性双向绑定到另一个 UserControl 的 DataContext 的属性?

问题描述 投票:0回答:1

我有一个 UserControl,其代码隐藏已声明依赖属性。我还有另一个 UserControl 作为主 UI。

主 UI 用户控件已将其 DataContext 设置为视图模型,并且该视图模型具有属性 SelectedFile。主 UI 用户控件的 XAML 声明另一个用户控件并将依赖属性“SelectedFilePath”绑定到属性 SelectedFile。

我想将 UserControl FileSelectControl 与 UserControl MainView 封装在一起。理想情况下,FileSelectControl 可以包含选择文件所需的所有代码并将所需信息传递到 MainWindow。所以我需要子控件将其数据传递给父控件。

MainView.xaml:

<UserControl x:Class="Whiteking_UnitTest_Updater.Views.MainView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
            xmlns:local="clr-namespace:Whiteking_UnitTest_Updater.Views"
            xmlns:viewmodel="clr-namespace:Whiteking_UnitTest_Updater.ViewModels"
            mc:Ignorable="d" 
            d:DesignHeight="450" d:DesignWidth="800" Background="Black"
            d:DataContext="{d:DesignInstance Type=viewmodel:MainViewModel}">
    <Grid>
        <Border BorderBrush="Lime" BorderThickness="1"/>
        <local:FileSelectControl Height="30" Width="500" SelectedFilepath="{Binding SelectedFile, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBox Text="{Binding SelectedFile, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,100,0,0"/>

        <Button x:Name="CloseButton"
                Content="EXIT"
                Margin="50,35"
                Padding="15,5"
                FontSize="18"
                HorizontalAlignment="Right"
                VerticalAlignment="Bottom"
                Style="{StaticResource Button_Custom}"
                Command="{Binding ExitCommand}"/>
    </Grid>
</UserControl>

MainView.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Whiteking_UnitTest_Updater.ViewModels;

namespace Whiteking_UnitTest_Updater.Views
{
    /// <summary>
    /// Interaction logic for MainView.xaml
    /// </summary>
    public partial class MainView : UserControl
    {
        public MainView()
        {
            InitializeComponent();
            this.DataContext = new MainViewModel();
        }
    }
}

文件选择控件.xaml:

<UserControl x:Class="Whiteking_UnitTest_Updater.Views.FileSelectControl"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
            xmlns:local="clr-namespace:Whiteking_UnitTest_Updater.Views"
            d:DataContext="{d:DesignInstance Type=local:FileSelectControl}"
            mc:Ignorable="d" 
            d:DesignHeight="30"
            d:DesignWidth="300">
    <Grid>
        <Button x:Name="FileSelectButton"
                Grid.Column="1"
                Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:FileSelectControl}}, Path=SelectedFilepath, UpdateSourceTrigger=PropertyChanged}"
                Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:FileSelectControl}}, Path=SelectFileCommand}">
            <Button.Style>
                <Style TargetType="{x:Type Button}">
                    <Setter Property="OverridesDefaultStyle" Value="True"/>
                    <Setter Property="SnapsToDevicePixels" Value="True"/>
                    <Setter Property="Background" Value="Black"/>
                    <Setter Property="Foreground" Value="Lime"/>
                    <Setter Property="BorderBrush" Value="Lime"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type Button}">
                                <Border x:Name="Container"
                                        BorderThickness="1"
                                        Background="{TemplateBinding Background}"
                                        BorderBrush="{TemplateBinding BorderBrush}">
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition/>
                                            <ColumnDefinition Width="auto"/>
                                        </Grid.ColumnDefinitions>
                                        <TextBlock x:Name="Content"
                                                    Grid.Column="0"
                                                    Padding="5,5,20,5"
                                                    VerticalAlignment="Center"
                                                    FontSize="{TemplateBinding FontSize}"
                                                    FontWeight="{TemplateBinding FontWeight}"
                                                    Text="{TemplateBinding Content}"/>
                                        <Border Grid.Column="1"
                                                BorderThickness="1,0,0,0"
                                                Background="{TemplateBinding Background}"
                                                BorderBrush="{TemplateBinding Foreground}"
                                                Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Grid}}, Path=ActualHeight}">
                                            <Path x:Name="Search_Icon"
                                                    Margin="5"
                                                    Stretch="Uniform"
                                                    Fill="{TemplateBinding Foreground}">
                                                <Path.Data>
                                                    M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z
                                                </Path.Data>
                                            </Path>
                                        </Border>
                                    </Grid>
                                </Border>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                    <Style.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="Lime"/>
                            <Setter Property="Foreground" Value="Black"/>
                            <Setter Property="BorderBrush" Value="Lime"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter Property="BorderBrush" Value="Red"/>
                            <Setter Property="Foreground" Value="Red"/>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
        </Button>
    </Grid>
</UserControl>

FileSelectControl.xaml.cs:

using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using Whiteking_UnitTest_Updater.Services;


namespace Whiteking_UnitTest_Updater.Views
{
    /// <summary>
    /// Interaction logic for FileSelectControl.xaml
    /// </summary>
    public partial class FileSelectControl : UserControl, INotifyPropertyChanged
    {
        public FileSelectControl()
        {
            InitializeComponent();
            SelectFileCommand = new RelayCommand(SelectFile);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
        {
            Trace.WriteLine("PropertyChanged called, property Name: " + propertyName);
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        #region DependencyProperty_SelectedFilepath
        public string SelectedFilepath
        {
            get => (string)GetValue(SelectedFilepathProperty);
            set
            {
                Trace.WriteLine("SelectedFilepathProperty SET was called");
                SetValue(SelectedFilepathProperty, value);
                NotifyPropertyChanged();
            }
        }
        public static readonly DependencyProperty SelectedFilepathProperty = DependencyProperty.Register(
            "SelectedFilepath",
            typeof(string),
            typeof(FileSelectControl),
            new PropertyMetadata(""));
        #endregion

        public RelayCommand SelectFileCommand { get; private set; }
        private void SelectFile(object sender)
        {
            string filepath;

            OpenFileDialog dialogue = new OpenFileDialog();
            bool? results = dialogue.ShowDialog();

            if (results == true)
            {
                //Get the path of specified file
                filepath = dialogue.FileName;
                SelectedFilepath = filepath;
            }
        }
    }
}

我的代码构建没有任何错误,绑定甚至可以单向工作,我可以在主 UI 用户控件中编辑文本框,它设置依赖属性,并且更改按照我的预期可视化。

问题是,当具有依赖属性的 UserControl 通过 RelayCommand 自行设置其属性时,这不会冒泡到父 USerControl 并更改绑定属性。

我很困惑,我尝试过捏造代码......但无济于事。

我希望有人能帮忙解决这个问题。我希望我的用户控件“FileSelectControl”能够在选择文件时触发 selectedFilePath 属性进行更改,并影响父级中的绑定参数。

c# wpf data-binding dependency-properties
1个回答
0
投票

MainView
中的绑定应该是
TwoWay
:

<local:FileSelectControl Height="30" Width="500"
    SelectedFilepath="{Binding SelectedFile, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

或者,您可以在

FileSelectControl
中定义默认双向绑定的依赖属性:

public static readonly DependencyProperty SelectedFilepathProperty = DependencyProperty.Register(
    "SelectedFilepath",
    typeof(string),
    typeof(FileSelectControl),
    new FrameworkPropertyMetadata("") { BindsTwoWayByDefault = true });

另请注意,当控件设置依赖属性时,没有理由实现

INotifyPropertyChanged

public partial class FileSelectControl : UserControl
{
    public FileSelectControl()
    {
        InitializeComponent();
        SelectFileCommand = new RelayCommand(SelectFile);
    }

    #region DependencyProperty_SelectedFilepath
    public string SelectedFilepath
    {
        get => (string)GetValue(SelectedFilepathProperty);
        set => SetValue(SelectedFilepathProperty, value);
    }
    public static readonly DependencyProperty SelectedFilepathProperty = DependencyProperty.Register(
        "SelectedFilepath",
        typeof(string),
        typeof(FileSelectControl),
        new FrameworkPropertyMetadata("") { BindsTwoWayByDefault = true  });
    #endregion

    public RelayCommand SelectFileCommand { get; private set; }
    
    private void SelectFile(object sender)
    {
        string filepath;

        OpenFileDialog dialogue = new OpenFileDialog();
        bool? results = dialogue.ShowDialog();

        if (results == true)
        {
            //Get the path of specified file
            filepath = dialogue.FileName;
            SelectedFilepath = filepath;
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.