DependencyProperties在UserControl中为null,但仅在公共属性中

时间:2019-04-22 12:56:40

标签: c# wpf

我正在尝试创建某种翻译字段,具体取决于当前语言和翻译语言,以显示占位符文本或翻译后的文本。

这是我在WPF中的第一个项目,因此,大多数时候我总是盲目寻找各种问题的解决方案,但我可以描述工作流我是如何做到的:

  1. 用户加载翻译文件 左侧显示了树,并且根据主视图中选定的树级别,他应该从一个到多个翻译字段中看到。

  2. 在导航栏中,用户可以更改当前语言,这将导致所有字段显示该语言的翻译,或者如果缺少翻译,则显示默认语言的“占位符”。

  3. 用户可以更改翻译字段的值以更改当前语言的翻译。

所以我将MainWindow分为3部分:

  • 顶部的菜单栏

  • 左侧的TreeView

  • ScrollViewer,右侧带有ItemsControl

由于它不是商业项目,因此下面的屏幕截图可以使您更好地理解: AppScreenshot

这是我的TranslationBox.xaml:

<UserControl x:Class="gmc_translator.TranslationBox"
             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:gmc_translator"
             xmlns:model="clr-namespace:gmc_translator.TreeView.Model"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
        <TextBox Text="{Binding TranslationLabelText}"
                 Width="300"
                 Height="100"
                 TextWrapping="Wrap"
                 Margin="20 20 20 0"
                 HorizontalAlignment="Left"
                 Background="#4D4D4D"
                 BorderThickness="0"
                 Name="Box"
                 GotFocus="Box_OnGotFocus"
                 LostFocus="Box_OnLostFocus">
            <TextBox.Style>
                <Style TargetType="{x:Type TextBox}">
                    <Setter Property="Foreground" Value="#B0B0B0" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Path=IsPlaceholder}" Value="True">
                            <Setter Property="Foreground" Value="#707070" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TextBox.Style>
        </TextBox>
    </Grid>

</UserControl>

TranslationBox.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using gmc.script;

namespace gmc_translator {

    public partial class TranslationBox : UserControl {

        public TranslationBox() {
            InitializeComponent();
        }

        public static readonly DependencyProperty CurrentLangProperty =
            DependencyProperty.Register("CurrentLang", typeof(string),
                typeof(TranslationBox), new FrameworkPropertyMetadata("", OnCurrentLangPropertyChanged));

        public static readonly DependencyProperty DefaultLangProperty =
            DependencyProperty.Register("DefaultLang", typeof(string),
                typeof(TranslationBox), new FrameworkPropertyMetadata("", OnDefaultLangPropertyChanged));

        public static readonly DependencyProperty TranslationLabelProperty =
            DependencyProperty.Register("TranslationLabel", typeof(TranslationLabel),
                typeof(TranslationBox), new FrameworkPropertyMetadata(new TranslationLabel()));

        private static readonly DependencyPropertyKey IsPlaceholderPropertyKey
            = DependencyProperty.RegisterReadOnly("IsPlaceholder", typeof(bool), typeof(TranslationBox),
                new FrameworkPropertyMetadata(default(bool),
                    FrameworkPropertyMetadataOptions.None));

        public static readonly DependencyProperty IsPlaceholderProperty
            = IsPlaceholderPropertyKey.DependencyProperty;

        public bool IsPlaceholder
        {
            get => (bool)GetValue(IsPlaceholderProperty);
            protected set => SetValue(IsPlaceholderPropertyKey, value);
        }

        public string CurrentLang {
            get => GetValue(CurrentLangProperty) as string;
            set => SetValue(CurrentLangProperty, value);
        }

        public string DefaultLang {
            get => GetValue(DefaultLangProperty) as string;
            set => SetValue(DefaultLangProperty, value);
        }

        public TranslationLabel TranslationLabel {
            get => GetValue(TranslationLabelProperty) as TranslationLabel;
            set => SetValue(TranslationLabelProperty, value);
        }

        public string TranslationLabelText {
            get => TranslationLabel.GetTranslationByLang(CurrentLang);
            set => TranslationLabel.SetForLanguage(CurrentLang, value);
        }

        public string TranslationLabelDefaultLangText => TranslationLabel.GetTranslationByLang(DefaultLang);

        private static void OnDefaultLangPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
            var o1 = (TranslationBox) d;
            o1.IsPlaceholder = o1.Box.Text.Equals(o1.TranslationLabelDefaultLangText) && !o1.DefaultLang.Equals(o1.CurrentLang);
        }

        private static void OnCurrentLangPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs args) {
            var o1 = (TranslationBox) d;
            o1.TranslationLabelText = o1.TranslationLabel.GetTranslationByLang(o1.CurrentLang);
            o1.IsPlaceholder = o1.TranslationLabelText.Equals(o1.TranslationLabelDefaultLangText) && !o1.DefaultLang.Equals(o1.CurrentLang);
        }

    }

}

现在,当我通过绑定更改样式时:

public bool IsPlaceholder => Box.Text.Equals(TranslationLabelDefaultLangText) && !DefaultLang.Equals(Lang);

DefaultLang和Lang为null,我不知道为什么在其他地方它能正常工作……至少是偏偏的。 :P

对我来说,看起来像是在正确设置DependencyProperties之前使用了IsPlaceholder,但是它从未更新。甚至当我更改或删除TextBox内容时。

我希望每次更改DefaultLang和Lang都会更新样式,并在框中测量值,但一次只能做一件事。

Edit1:

MainWindow.xaml.cs

using System.Windows;
using System.Windows.Controls;
using gmc.loaders;
using gmc_translator.TreeView.Model;
using Microsoft.Win32;

namespace gmc_translator {

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow {

        public static readonly DependencyProperty DefaultLangProperty =
            DependencyProperty.Register("DefaultLang", typeof(string),
                typeof(MainWindow), new FrameworkPropertyMetadata(null));

        public static readonly DependencyProperty CurrentLangProperty =
            DependencyProperty.Register("CurrentLang", typeof(string),
                typeof(MainWindow), new FrameworkPropertyMetadata(null));

        public string DefaultLang {
            get => GetValue(DefaultLangProperty) as string;
            set => SetValue(DefaultLangProperty, value);
        }

        public string CurrentLang {
            get => GetValue(CurrentLangProperty) as string;
            set => SetValue(CurrentLangProperty, value);
        }

        public MainWindow() {
            InitializeComponent();
            Title = "GMC";
            WindowStartupLocation = WindowStartupLocation.CenterScreen;

        }

        private void MenuItem_OnClick(object sender, RoutedEventArgs e) {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            if (openFileDialog.ShowDialog() == true) {
                var translationFile = TranslationFileLoader.LoadTranslationFile(openFileDialog.FileName);
                var view = new ScriptTreeViewModel(translationFile);

                view.AvailableLanguages.Add("en_US");
                Langs.Items.Clear();
                view.AvailableLanguages.ForEach(lang => {
                    MenuItem item = new MenuItem {Header = lang, IsCheckable = true};
                    item.Click += ChangeCurrentLanguage;
                    Langs.Items.Add(item);
                });
                ((MenuItem) Langs.Items.GetItemAt(0)).IsChecked = true;
                DefaultLang = translationFile.DefaultLanguage;
                CurrentLang = ((MenuItem) Langs.Items.GetItemAt(0)).Header as string;

                Nav.DataContext = view;
            }

        }

        private void ChangeCurrentLanguage(object sender, RoutedEventArgs e) {
            foreach (var langItem in Langs.Items) {
                ((MenuItem) langItem).IsChecked = false;
            }

            ((MenuItem) sender).IsChecked = true;
            CurrentLang = ((MenuItem) sender).Header as string;
        }

        private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) {
            if (!(e.NewValue is ScriptViewModel item)) return;
            MainTranslationView.Items.Clear();
            MainTranslationView.Items.Add(item);
        }

    }

}

MainWindow.xaml

<Window x:Class="gmc_translator.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:model="clr-namespace:gmc_translator.TreeView.Model"
        xmlns:script="clr-namespace:gmc.script;assembly=gmc"
        xmlns:gmcTranslator="clr-namespace:gmc_translator"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" Height="600" Width="1200" Background="#808080" x:Name="Window">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="24" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Menu DockPanel.Dock="Top" Background="#404040" FontSize="16">
            <MenuItem Header="_File" Background="#4D4D4D" Foreground="#CCCCCC">
                <MenuItem Header="_Open" IsEnabled="True" Click="MenuItem_OnClick" />
                <MenuItem Header="_Import" />
                <MenuItem Header="_Export" />
                <MenuItem Header="_Save" />
            </MenuItem>

            <MenuItem Header="_Lang" Background="#4D4D4D" Foreground="#CCCCCC" Name="Langs" />
        </Menu>

        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="400" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Rectangle Grid.Column="0" Grid.RowSpan="1000" Fill="#4D4D4D" />

            <TreeView Grid.Column="0" x:Name="Nav" ItemsSource="{Binding FirstGeneration}"
                      d:DataContext="{d:DesignInstance model:ScriptTreeViewModel}"
                      SelectedItemChanged="TreeView_OnSelectedItemChanged" Margin="20" Background="#333333"
                      BorderThickness="0">
                <TreeView.ItemContainerStyle>
                    <Style TargetType="{x:Type TreeViewItem}">
                        <Setter Property="FontWeight" Value="Normal" />
                        <Setter Property="FontSize" Value="12" />
                        <Setter Property="Foreground" Value="#B0B0B0" />
                        <Style.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="FontWeight" Value="Bold" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </TreeView.ItemContainerStyle>


                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate ItemsSource="{Binding (model:ScriptViewModel.Children)}">
                        <TextBlock Text="{Binding Name}" />
                    </HierarchicalDataTemplate>
                </TreeView.ItemTemplate>
            </TreeView>

            <ScrollViewer Grid.Column="1" VerticalScrollBarVisibility="Auto">
                <ItemsControl Name="MainTranslationView">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <VirtualizingStackPanel />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>

                    <ItemsControl.Resources>


                        <DataTemplate DataType="{x:Type model:ScriptViewFile}">
                            <TextBox Text="{Binding Name, Mode=OneWay}" Width="300" Height="100" TextWrapping="Wrap"
                                     Margin="20 20 20 0" HorizontalAlignment="Left" Background="#4D4D4D"
                                     BorderThickness="0" Foreground="#B0B0B0" />
                        </DataTemplate>


                        <DataTemplate DataType="{x:Type model:ScriptViewModelDialogueLine}">
                            <gmcTranslator:TranslationBox
                                Lang="{Binding Path=CurrentLang, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gmcTranslator:MainWindow}}}" 
                                DefaultLang="{Binding Path=DefaultLang, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gmcTranslator:MainWindow}}}" 
                                TranslationLabel="{Binding Path=Line.Translation}"/>
                        </DataTemplate>


                        <DataTemplate DataType="{x:Type model:ScriptViewTranslation}">
                            <TextBox Text="Random Text" Width="300" Height="100" TextWrapping="Wrap"
                                     Margin="20 20 20 0" HorizontalAlignment="Left" Background="#4D4D4D"
                                     BorderThickness="0" Foreground="#B0B0B0" />
                        </DataTemplate>


                        <DataTemplate DataType="{x:Type model:ScriptViewFunction}">
                            <ItemsControl ItemsSource="{Binding Path=ScriptFunction.Labels}">
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <gmcTranslator:TranslationBox
                                            TranslationLabel="{Binding Path=Translation}"
                                            DefaultLang="{Binding Path=DefaultLang, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gmcTranslator:MainWindow}}}" 
                                            Lang="{Binding Path=CurrentLang, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gmcTranslator:MainWindow}}}" />
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </DataTemplate>


                    </ItemsControl.Resources>
                </ItemsControl>
            </ScrollViewer>

        </Grid>


    </Grid>
</Window>

Edit3:更新了代码段,删除了重复的代码段

0 个答案:

没有答案