首页 文章

对于子ViewModel属性,PropertyChanged值为null

提问于
浏览
0

我的主ViewModel中有一个ViewModel的ObservableCollection . 绑定似乎工作正常,因为我可以切换视图 . 但是,在ObservableCollection的元素中引发ViewModelBase OnPropertyChanged方法(适用于其他内容)会导致ViewModelBase中的null PropertyChanged值 .

这是我的主要代码片段:

In my main ViewModel 构造函数:

public EditorViewModel()
    {
        base.DisplayName = Strings.EditorName;

        _availableEditors = new ObservableCollection<ViewModelBase>();

        AvailableEditors.Add(new GBARomViewModel(646, 384));
        AvailableEditors.Add(new MonsterViewModel(800, 500));

        CurrentEditor = _availableEditors[0];
    }

在GBA ROM加载中,ViewModel和Model属性更新:

void RequestOpenRom()
    {
        OpenFileDialog dlg = new OpenFileDialog();
        dlg.DefaultExt = ".gba";
        dlg.Filter = "GBA ROM (.gba)|*.gba|All files (*.*)|*.*";
        dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

        Nullable<bool> result = dlg.ShowDialog();

        if (result == true)
        {
            if(CurrentEditor is GBARomViewModel)
            {
                (CurrentEditor as GBARomViewModel).ReadRom(dlg.FileName);
            }               
        }
    }

In my main View: TabControl的变化(用于保持视图切换和视图状态) .

<controls:TabControlEx ItemsSource="{Binding AvailableEditors}"
        SelectedItem="{Binding CurrentEditor}"
        Style="{StaticResource BlankTabControlTemplate}"
        MinWidth="{Binding CurrentEditorWidth}"
        MinHeight="{Binding CurrentEditorHeight}"
        MaxWidth="{Binding CurrentEditorWidth}"
        MaxHeight="{Binding CurrentEditorHeight}"
        Width="{Binding CurrentEditorWidth}"
        Height="{Binding CurrentEditorHeight}"
        HorizontalAlignment="Left"
        VerticalAlignment="Top">
        <controls:TabControlEx.Resources>
            <DataTemplate DataType="{x:Type vm:GBARomViewModel}">
                <vw:GBARomView />
            </DataTemplate>
            <DataTemplate DataType="{x:Type vm:MonsterViewModel}">
                <vw:MonsterView />
            </DataTemplate>
        </controls:TabControlEx.Resources>
    </controls:TabControlEx>

In GBARomViewModel (子ViewModel,AvailableEditors的元素)

public String CRC32
    {
        get
        {
            return _rom.CRC32;
        }
        set
        {
            if (value.Equals(_rom.CRC32))
            {
                return;
            }

            _rom.CRC32 = value;
            OnPropertyChanged("CRC32");
        }
    }

Property binding in child View
现在这是一个UserControl,所以我也会把它的代码放在后面 . 启动时的其他属性,如LabelWidth和LabelValue . 在XAML中为TextBoxValue提供默认值也可以 .

<StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="10, 0, 0, 10" Width="300">
        <dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomTitle}" TextBoxValue="{Binding Title}" />
        <dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomGameCode}" TextBoxValue="{Binding GameCode}" />
        <dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomRomSize}" TextBoxValue="{Binding RomSize}"  />
        <dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomCRC32}" TextBoxValue="{Binding CRC32}"  />
        <dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="200" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomMD5Checksum}" TextBoxValue="{Binding MD5Checksum}"/>
    </StackPanel>

DefaultLabelBox.cs

<UserControl x:Name="uc">
    <StackPanel>
        <TextBlock Text="{Binding Path=LabelValue, ElementName=uc}"
           Width="{Binding Path=LabelWidth, ElementName=uc}"/>
        <Label Content="{Binding Path=TextBoxValue, Mode=OneWay, ElementName=uc}"
         Width="{Binding Path=TextBoxWidth, ElementName=uc}"/>
    </StackPanel>
</UserControl>

DefaultLabelBox.xaml.cs

public string TextBoxValue
    {
        get {
            return (string)GetValue(TextBoxValueProperty);
        }
        set {
            SetValue(TextBoxValueProperty, value);
        }
    }

    public static readonly DependencyProperty TextBoxValueProperty =
        DependencyProperty.Register("TextBoxValue", typeof(string), typeof(DefaultLabelBox), new PropertyMetadata(default(string)));

Control Template

<Style TargetType="dlb:DefaultLabelBox">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="dlb:DefaultLabelBox">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding LabelValue, RelativeSource={RelativeSource TemplatedParent}}"
                               MinWidth="20"
                               Width="{Binding LabelWidth, RelativeSource={RelativeSource TemplatedParent}}"
                               VerticalAlignment="Center"
                               FontFamily="Mangal"
                               Height="20"
                               FontSize="13"/>
                    <Label Content="{Binding TextBoxValue, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
                           BorderBrush="{StaticResource DefaultLabelBoxBorderBrush}"
                           BorderThickness="1"
                           Padding="1,1,1,1"
                           Background="{StaticResource DefaultLabelBoxBackgroundBrush}"
                           Foreground="{StaticResource DefaultLabelBoxForeground}"
                               MinWidth="60"
                               Height="20"
                               VerticalAlignment="Center"
                               FontFamily="Mangal"
                               Width="{Binding TextBoxWidth, RelativeSource={RelativeSource TemplatedParent}}" 
                               FontSize="13"/>
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

我尝试了一些东西但是对MVVM不熟悉我不知道我是否有一个绑定的DataContext问题 . 任何帮助,将不胜感激 .

Edit: 我对一些代码进行了更改,以便为我说明工作解决方案以及添加我忘记的ControlTemplate . 我现在正在工作,所以我将它原样保留下来 .

1 回答

  • 1

    为了使绑定像

    <dlb:DefaultLabelBox ... TextBoxValue="{Binding CRC32, Mode=TwoWay}" />
    

    在工作中,DefaultLabelBox需要从其父控件继承其DataContext(这是顺便说一句 . 这是UserControl永远不应该显式设置其DataContext的原因) .

    但是,UserControl的XAML中的“内部”绑定需要显式指定的Source或RelativeSource或ElementName .

    所以他们应该(例如)看起来像这样:

    <UserControl ... x:Name="uc">
        <StackPanel>
            <TextBlock
                 Text="{Binding Path=LabelValue, ElementName=uc}"
                 Width="{Binding Path=LabelWidth, ElementName=uc}"/>
            <TextBox
                Text="{Binding Path=TextBoxValue, Mode=TwoWay, ElementName=uc}"
                Width="{Binding Path=TextBoxWidth, ElementName=uc}"/>
        </StackPanel>
    </UserControl>
    

相关问题