XAML DataTemplate(TabControl.ContentTemplate)中的UserControlDependencyProperty绑定

ql3eal8s  于 2023-08-01  发布在  其他
关注(0)|答案(1)|浏览(113)

我用DependencyProperty创建了我的UserControl。当我在TabControl.ContentTemplate的DataTemplate中使用它时,数据绑定仍然停留在第一个选项卡对象上。文本框数据绑定工作正常。
我想我错过了一些关于UI虚拟化的东西。我注意到所有的标签都使用相同的ModeButton用户控件。
ModeButton.xaml

<UserControl x:Class="SSI.GUI.Controls.ProfileParameterButton"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Button x:Name="button" Margin="0" Padding="0" Click="button_Click"/>
</UserControl>

字符串
ModeButton.xaml.cs

public partial class ProfileParameterButton : UserControl
{
    public ProfileParameterButton()
    {
        InitializeComponent();
    }

    #region ProfileParameter

    public IProfileParameter? ProfileParameter
    {
        get { return GetValue(ProfileParameterProperty) is IProfileParameter value ? value : default; }
        set { SetValue(ProfileParameterProperty, value); }
    }

    public static readonly DependencyProperty ProfileParameterProperty = DependencyProperty.Register(
        nameof(ProfileParameter),
        typeof(IProfileParameter),
        typeof(ProfileParameterButton),
        new FrameworkPropertyMetadata(
            default,
            FrameworkPropertyMetadataOptions.None,
            new PropertyChangedCallback(OnProfileParameterChanged)
    ));

    private static void OnProfileParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is ProfileParameterButton instance)
        {
            if (e.OldValue is INotifyPropertyChanged oldNPC)
            {
                oldNPC.PropertyChanged -= instance.ProfileParameter_PropertyChanging;
            }
            if (e.NewValue is IProfileParameter newNPC)
            {
                newNPC.PropertyChanged += instance.ProfileParameter_PropertyChanging;
            }
            instance.ProfileParameter = e.NewValue as IProfileParameter;
            instance.ModeChanged();
        }
        else throw new InvalidOperationException();
    }

    private void ProfileParameter_PropertyChanging(object? sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(IProfileParameter.Mode))
        {
            ModeChanged();
        }
    }

    #endregion ProfileParameter

    private void ModeChanged()
    {
        if (ProfileParameter == null)
        {
            button.ToolTip = Lang.Disabled;
            button.IsEnabled = false;
        }
        else
        {
            button.ToolTip = ProfileParameter.Mode.ToString();
            button.IsEnabled = ProfileParameter.CanReset();
        }
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
        ProfileParameter?.Reset();
    }
}


TabControl:

<TabControl ItemsSource="{Binding MyData}">
    <TabControl.ContentTemplate>
        <DataTemplate DataType="{x:Type viewmodelsData:MyData}">
            <UniformGrid Columns="2">
                <StackPanel>
                    <TextBlock Text="{Binding Material.Name}" FontWeight="Bold" FontSize="18"/>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <!--Row-->
                        <controls:ModeButton Grid.Row="0" Grid.Column="1" Margin="2,2" ProfileParameter="{Binding Material.Parameter1}"/>
                        <TextBlock Grid.Row="0" Grid.Column="0" Margin="2,2" Text="{DynamicResource Lang.Material.Parameter1}" VerticalAlignment="Center"/>
                        <TextBox   Grid.Row="0" Grid.Column="2" Margin="2,2" Text="{Binding Material.Parameter1.Value}" HorizontalContentAlignment="Right"/>
                        <!--Row-->
                        <controls:ModeButton Grid.Row="1" Grid.Column="1" Margin="2,2" ProfileParameter="{Binding Material.Parameter2}"/>
                        <TextBlock Grid.Row="1" Grid.Column="0" Margin="2,2" Text="{DynamicResource Lang.Material.Parameter2}" VerticalAlignment="Center"/>
                        <TextBox   Grid.Row="1" Grid.Column="2" Margin="2,2" Text="{Binding Material.Parameter2.Value}" HorizontalContentAlignment="Right"/>
                        <TextBlock Grid.Row="1" Grid.Column="3" Margin="2,2" Text="[s]" VerticalAlignment="Center"/> 
            </UniformGrid>
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

dxxyhpgq

dxxyhpgq1#

声明

instance.ProfileParameter = e.NewValue as IProfileParameter;

字符串
将新值分配给ProfileParameter属性,该属性将替换任何非TwoWay的Binding。
这个赋值无论如何都是无用的,因为PropertyChangedCallback是在属性值已经更改时调用的。不需要重新分配该值。
您的代码似乎还暗示IProfileParameter是从INotifyPropertyChanged派生的,这使得PropertyChangedCallback中的所有类型检查都毫无意义。
依赖属性系统保证DependencyObject参数始终是ProfileParameterButton,e.OldValuee.NewValue属性始终是IProfileParameter类型。
因此,你可以这样写回调:

private static void OnProfileParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var instance = (ProfileParameterButton)d;

    if (e.OldValue != null)
    {
        ((IProfileParameter)e.OldValue).PropertyChanged -= instance.ProfileParameter_PropertyChanging;
    }

    if (e.NewValue != null)
    {
        ((IProfileParameter)e.NewValue).PropertyChanged += instance.ProfileParameter_PropertyChanging;
    }

    instance.ModeChanged();
}

相关问题