我正在尝试创建某种翻译字段,具体取决于当前语言和翻译语言,以显示占位符文本或翻译后的文本。
这是我在WPF中的第一个项目,因此,大多数时候我总是盲目寻找各种问题的解决方案,但我可以描述工作流我是如何做到的:
用户加载翻译文件 左侧显示了树,并且根据主视图中选定的树级别,他应该从一个到多个翻译字段中看到。
在导航栏中,用户可以更改当前语言,这将导致所有字段显示该语言的翻译,或者如果缺少翻译,则显示默认语言的“占位符”。
用户可以更改翻译字段的值以更改当前语言的翻译。
所以我将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:更新了代码段,删除了重复的代码段