@dovid כתב באיך לשנות את צבע הרקע (Background) של פקד קומבו (ComboBox) ב-Wpf:
PART_EditableTextBox
זה לא מופיע לי. אולי כי יש לי כבר קצת סגנון על הקומבו?
@dovid כתב באיך לשנות את צבע הרקע (Background) של פקד קומבו (ComboBox) ב-Wpf:
PART_EditableTextBox
זה לא מופיע לי. אולי כי יש לי כבר קצת סגנון על הקומבו?
@dovid כתב באיך לשנות את צבע הרקע (Background) של פקד קומבו (ComboBox) ב-Wpf:
אני חושב שאתה לא עושה את 3, כי זה לא מפרק כלום. זה פשוט מעתיק את הטמפלייט לפרוייקט שלך כעותק, הפקד אמור להיות 1000% אותו דבר.
צודק, אכן הצלחתי לשנות את הרקע כך.
אבל יש חלק של הפקד שאני לא מצליח למצוא: מופיע ה-TextBox של ה-Editable ComboBox, וזה גורם בעיות בהצגת הטקסט בצבעים מסוימים. (ולכן גם נסיתי להוסיף את החלק הזה בתוך ה-template באופן ידני וזה מה שגרם לכל הבעיות).
בקיצור: איך אני משנה את צבע הטקסט?
@dovid
3 היא התשובה ל-2 כלומר לזה התכוונתי בלפרק פקד דהיינו על ידי עריכת ה-template שלו.
למעישה שמעתי לעצתך ובדקתי שוב ומתברר שעיקר העיכוב הוא כאשר ה-viewmodel מחובר. אעבור עליו כעת ונראה מה אפשר לעשות.
עריכה: לאחר ריקון כל ה-viewmodel הטעינה הייתה עדיין איטית
לעומת זאת טעינה של ה-viewmodel דרך ה-codebehind שיפר את הזמן טעינה בכחצי עדיין לוקח 4 שניות לטעון - זה נקרא איטי או לא?
אולי זה קשור לשגיאה הזו שמופיעה כאשר אני טוען בתוך ה-xaml
Object reference not set to an instance of an object.
לא מצאתי דרך ישירה לשנות את צבע הרקע של פקד ComboBox ב-WPF, (לא ה-dropdown אלא הפקד עצמו) משום שהוא בנוי ממספר פקדים ומשום שיש בו טריגרים שמונעים זאת לכאורה.
האם מישהו מכיר דרך לשנות את הרקע מבלי לפרק את הפקד ולבנות אותו מחדש?
(פירוק הפקד גורם לאיטיות בטעינה, והפקד גם כך איטי בטעינה — מדוע טעינה של WPF כל כך איטית?
)
תודה מראש!
@קומפיונט
שוב תודה על העזרה המדהימה
מה ההסבר למה בעצם אני יכול לקצר את זה? הרי הוא נמצא בתוך תיקייה וnamespace שנקראת locale זה לא יגרום בעיות?
השתמשתי ב-oncurrent dictionary כי לא ידעתי אם יכול להיות בעיות עם Threding אזמח לשמוע אם אתה בטוח שזה לא נצרך.
סקיצה סופית על ידי markupExtension ו-יצירת binding דינאמי. עובד גם על ב-designtime.
LocaleDictionary.cs
LocaleExtension.cs
RelayCommand.cs
זמין גם בגיטהאב
https://github.com/pcinfogmach/Wpf.Localization
דוגמת שימוש
<Window x:Class="localizationTestApp.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:local="clr-namespace:localizationTestApp"
xmlns:loc="clr-namespace:localization;assembly=localization"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Button Width="200" Height="50"
Content="{loc:LocaleExtension Text=Login}"
Command="{x:Static loc:LocaleDictionary.NextLocaleCommand}"/>
<Button Width="200" Height="50"
Content="Load Hebrew"
Command="{x:Static loc:LocaleDictionary.ChangeLocaleCommand}"
CommandParameter="he"/>
<TextBlock Margin="10"
Text="{loc:LocaleExtension Text=Welcome}"/>
<TextBox Width="200"
Text="{loc:LocaleExtension Text=Placeholder}"/>
<ComboBox Width="200" IsEditable="True"
ItemsSource="{x:Static loc:LocaleDictionary.LocaleList}"
Text="{Binding Path=(loc:LocaleDictionary.Locale)}"/>
</StackPanel>
</Window>
@pcinfogmach כתב במדריך: איך לייצר לוקליזציה ב-wpf בצורה פשוטה וקלילה:
האם Application.Current.Resources. זמין ב-Vsto (תוספים לאופיס) שזה בעצם אפליקצייה של winforms שתומכת ב-wpf.
כאן יש כמה רעיונות:
https://drwpf.com/blog/2007/10/05/managing-application-resources-when-wpf-is-hosted/
רק נשאר הבעיה המצבנת של designtime
@מד
עשיתי את זה ולמחרת חשבון המייקרוסופט שלי נחסם עם הודעה שעשיתי שימוש לא ראוי בחשבון 
@קומפיונט כתב במדריך: איך לייצר לוקליזציה ב-wpf בצורה פשוטה וקלילה:
@pcinfogmach מהקוד זה נראה שאתה שומר reference לכל הפקדים שצריכים להיות עם טקסט דינאמי, ומשנה אותם בהתאם מתי שצריך.
אתה יכול במקום זה להשתמש עם DynamicResource ובכל פעם שמוחלפת השפה לשנות את ה - Resrouce של השפה ב - Application.Current.Resources.
0
אכן זה עובד יפה ומייצר קוד הרבה יותר טוב. תודה.
אשמח אם תוכל לענות לי על כמה שאלות:
אני לא מצליח לטעון את זה ב-DesignTime זה מאוד מקשה על הפיתוח, מבחינת שגיאות לא אמיתיות ועוד ועוד. אלא אם כן אני ייצר rsourcedictionary ב-xaml כמו שמתארים פה ואז אני מאבד את כל הרעיון כי הרעיון היה לייצר קוד אחד שמתאים להרבה אפליקציות עם טעינה מקובץ json
האם Application.Current.Resources. זמין ב-Vsto (תוספים לאופיס) שזה בעצם אפליקצייה של winforms שתומכת ב-wpf.
להלן הקוד שיצרתי עם זה.
using Localization;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Windows;
namespace localization
{
public static class LocaleResouceManager
{
private static string _locale = "en";
private static readonly string _localeFolder = "Locale";
public static string Locale
{
get => _locale;
set
{
if (value != _locale)
{
_locale = value;
UpdateResources();
}
}
}
public static void UpdateResources()
{
string filePath = GetLocaleFilePath();
if (filePath == null)
return;
try
{
string json = File.ReadAllText(filePath);
var translations = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
if (translations != null)
{
var applicationResources = Application.Current.Resources;
foreach (var key in translations.Keys)
{
if (applicationResources.Contains(key))
applicationResources[key] = translations[key];
else
applicationResources.Add(key, translations[key]);
}
}
}
catch
{
// Handle errors (e.g., log or display error)
}
}
public static void NextLocale()
{
string localeFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), _localeFolder);
if (Directory.Exists(localeFolder))
{
var locales = Directory.GetFiles(localeFolder, "*.json")
.Select(Path.GetFileNameWithoutExtension)
.ToList();
if (locales.Count == 0)
return;
int currentIndex = locales.IndexOf(Locale);
if (currentIndex == -1 || currentIndex == locales.Count - 1)
Locale = locales[0];
else
Locale = locales[currentIndex + 1];
}
}
private static string GetLocaleFilePath()
{
string localeFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), _localeFolder);
string filePath = Path.Combine(localeFolder, _locale + ".json");
if (!File.Exists(filePath))
{
if (Directory.Exists(localeFolder))
{
string[] files = Directory.GetFiles(localeFolder, "*.json");
filePath = files.Length > 0 ? files[0] : null;
}
else
{
filePath = null;
}
}
return filePath;
}
}
}
@קומפיונט כתב במדריך: איך לייצר לוקליזציה ב-wpf בצורה פשוטה וקלילה:
תנסה ליישם משהו שמאפשר לשנות את השפה בצורה דינאמית, מבלי להפעיל מחדש
אחרי תקופה הייתי צריך משהו כזה מצו"ב הקוד:
את קבצי השפה של ה-json יש להכניס לתוך תיקייה בשם "Locale".
קישור לפרוייקט דוגמא
אשמח לקבל משוב
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Localization
{
public static class LocalizationExtension
{
private static string _locale = "en";
private static readonly string _localeFolder = "Locale";
private static ConcurrentDictionary<string, string> translations;
private static readonly List<WeakReference<DependencyObject>> RegisteredElements = new List<WeakReference<DependencyObject>>();
public static string Locale
{
get => _locale;
set
{
if (value != _locale)
{
_locale = value;
UpdateAllRegisteredElements();
}
}
}
public static readonly DependencyProperty KeyProperty =
DependencyProperty.RegisterAttached(
"Key",
typeof(string),
typeof(LocalizationExtension),
new PropertyMetadata(null, OnKeyChanged));
public static void SetKey(DependencyObject element, string value) =>
element.SetValue(KeyProperty, value);
public static string GetKey(DependencyObject element) =>
(string)element.GetValue(KeyProperty);
private static void OnKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d == null) return;
SetText(d, (string)e.NewValue);
CleanupRegisteredElements();
RegisteredElements.Add(new WeakReference<DependencyObject>(d));
}
public static RelayCommand<string> ChangeLocaleCommand = new RelayCommand<string>((value) => { Locale = value; } );
public static RelayCommand NextLocaleCommand = new RelayCommand(() => { NextLocale(); });
public static void NextLocale()
{
string localeFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), _localeFolder);
if (Directory.Exists(localeFolder))
{
var locales = Directory.GetFiles(localeFolder, "*.json")
.Select(Path.GetFileNameWithoutExtension)
.ToList();
if (locales.Count == 0)
return;
int currentIndex = locales.IndexOf(Locale);
if (currentIndex == -1 || currentIndex == locales.Count - 1)
Locale = locales[0];
else
Locale = locales[currentIndex + 1];
}
}
private static void UpdateAllRegisteredElements()
{
LoadTranslations();
CleanupRegisteredElements();
foreach (var weakReference in RegisteredElements)
{
if (weakReference.TryGetTarget(out var target))
{
var key = GetKey(target);
SetText(target, key);
}
}
}
private static void CleanupRegisteredElements() =>
RegisteredElements.RemoveAll(wr => !wr.TryGetTarget(out _));
public static void LoadTranslations()
{
string filePath = GetLocaleFilePath();
try
{
string json = File.ReadAllText(filePath);
translations = JsonSerializer.Deserialize<ConcurrentDictionary<string, string>>(json) ?? new ConcurrentDictionary<string, string>();
}
catch
{
translations = new ConcurrentDictionary<string, string>();
}
}
static string GetLocaleFilePath()
{
string localeFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), _localeFolder);
string filePath = Path.Combine(localeFolder, _locale + ".json");
if (!File.Exists(filePath))
{
if (Directory.Exists(localeFolder))
{
string[] files = Directory.GetFiles(localeFolder, "*.json");
if (files.Length > 0)
filePath = filePath = files[0];
else
filePath = null;
}
else
filePath = null;
}
return filePath;
}
public static void SetText(DependencyObject d, string key)
{
if (d == null || string.IsNullOrEmpty(key))
return;
if (translations == null)
LoadTranslations();
if (translations.TryGetValue(key, out var translation))
{
switch (d)
{
case ContentControl contentControl:
contentControl.Content = translation;
break;
default:
var textProperty = d.GetType().GetProperty("Text");
textProperty?.SetValue(d, translation);
break;
}
}
}
}
הקוד משתמש ב-class שנקרא RelayCommand
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
public void Execute(object parameter) => _execute();
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
public class RelayCommand<T> : ICommand
{
private readonly Action<T> _execute;
private readonly Func<T, bool> _canExecute;
public RelayCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute?.Invoke((T)parameter) ?? true;
public void Execute(object parameter) => _execute((T)parameter);
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
}
דוגמת שימוש:
<Button Width="200" Height="50"
locale:LocalizationExtension.Key="LoginButton"
Command="{x:Static locale:LocalizationExtension.NextLocaleCommand}"
@מד
שוב תודה.
יצרתי גרסה חדשה עם המתקין עובד חלק ויפה ואפשר להוסיף אייקון בקלות.
אגב אפשר לקמפל על ידי לחיצה ימנית על הקובץ לא צריך לפתוח את התוכנה.

ממש מרגש כל העזרה שקיבלתי פה מחברי הפורום המיוחדים - תודה!
להלן התוצאה לבינתיים:


@dovid
אפשר לכאורה לייצר פקד דינאמי מהרעיון הזה
using System.Windows.Controls;
using System.Windows;
public class ButtonStripGrid : Grid
{
private void UpdateLayoutColumns()
{
this.ColumnDefinitions.Clear();
int childrenCount = this.Children.Count;
if (childrenCount == 0)
return;
for (int i = 0; i < childrenCount * 2 - 1; i++)
{
this.ColumnDefinitions.Add(new ColumnDefinition
{
Width = (i % 2 == 0) ? GridLength.Auto : new GridLength(1, GridUnitType.Star)
});
}
for (int i = 0, j = 0; i < childrenCount; i++, j += 2)
{
SetColumn(this.Children[i], j);
}
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
UpdateLayoutColumns();
}
public ButtonStripGrid()
{
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
this.Loaded += (s, e) => UpdateLayoutColumns();
}
}
}
@קומפיונט
לא הבנתי את השאלה התוספת של ה-gridcolumns היא בין הלחצנים
wpf:GridSetup.Columns="auto,*,auto,*,auto,*,auto,*,auto,*,auto,*,auto,*,auto"
זה עובד נפלא!
@קומפיונט כתב במראה יותר אחיד ויפה לפקד ב-wpf:
אני לא בטוח שאני צודק, אבל אני חושב שאין כזה פנל ב-wpf.
לכאורה אפשר על ידי הגדרת Gridcolumns בין הלחצנים
@dovid כתב במראה יותר אחיד ויפה לפקד ב-wpf:
אני חושב שהמיכל צריך להיות לגמרי יחסי, ולא להסתמך על גודל מוחלט.
אתה צודק אבל בשלב מסויים זה נהיה מוגזם בהחלט ולא נראה יפה.
יש לי בעיה כללית שמסכים בגדלים שונים משנים את גודל החלונית צד ובצורה מוגזמת למדי.
מישהו האם יש צורה לצבוע צד אחד של המסגרת של פקד border ב-wpf בצבע אחר משאר הצדדים?