Я пытаюсь создать какое-то поле перевода, которое в зависимости от текущего языка и языка перевода показывало бы текст-заполнитель или переведенный текст.
Это мой первый проект в WPF, поэтому большую часть времени я слоняюсь без дела в поисках решения различных проблем, но могу описать рабочий процесс, как я пытаюсь это сделать:
Пользователь загружает файл перевода
С левой стороны отображается дерево, и в зависимости от выбранного уровня дерева на главном экране он должен видеть от одного до многих полей перевода.
Из навигационной панели пользователь может изменить текущий язык, и это приведет к тому, что во всех полях будут отображаться переводы на этом языке или «заполнитель» на языке по умолчанию, если перевод отсутствует.
Пользователь может изменить значение поля перевода, чтобы изменить перевод для текущего языка.
Итак, я делю MainWindow на 3 части:
Поскольку это не коммерческий проект, вот скриншот для лучшего понимания:
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 равны нулю, и я понятия не имею, почему, когда в других местах это работает ... По крайней мере, частично. : Р
Для меня это выглядит так, как будто IsPlaceholder используется до того, как DependencyProperties установлены правильно, но затем он никогда не обновляется. Даже когда я изменяю содержимое 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: обновлены фрагменты кода, удалены дублированные фрагменты кода