Skip to content

Commit b704c67

Browse files
author
Mostey
committed
Added Infralution.Wpf.Localization assembly for the Resx extension
1 parent a49d54c commit b704c67

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+6071
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
//
2+
// FILE: CultureManager.cs.
3+
//
4+
// COPYRIGHT: Copyright 2008
5+
// Infralution
6+
//
7+
using System;
8+
using System.Collections.Generic;
9+
using System.Globalization;
10+
using System.Threading;
11+
using System.Windows;
12+
using System.Windows.Markup;
13+
using System.Windows.Forms;
14+
using System.Reflection;
15+
using Infralution.Localization.Wpf.Properties;
16+
using System.Runtime.InteropServices;
17+
namespace Infralution.Localization.Wpf
18+
{
19+
20+
/// <summary>
21+
/// Provides the ability to change the UICulture for WPF Windows and controls
22+
/// dynamically.
23+
/// </summary>
24+
/// <remarks>
25+
/// XAML elements that use the <see cref="ResxExtension"/> are automatically
26+
/// updated when the <see cref="CultureManager.UICulture"/> property is changed.
27+
/// </remarks>
28+
public static class CultureManager
29+
{
30+
#region Static Member Variables
31+
32+
/// <summary>
33+
/// Current UICulture of the application
34+
/// </summary>
35+
private static CultureInfo _uiCulture;
36+
37+
/// <summary>
38+
/// The active design time culture selection window (if any)
39+
/// </summary>
40+
private static CultureSelectWindow _cultureSelectWindow;
41+
42+
/// <summary>
43+
/// The active task bar notify icon for design time culture selection (if any)
44+
/// </summary>
45+
private static NotifyIcon _notifyIcon;
46+
47+
/// <summary>
48+
/// The window handle for the notify icon
49+
/// </summary>
50+
private static IntPtr _notifyIconHandle;
51+
52+
/// <summary>
53+
/// Should the <see cref="Thread.CurrentCulture"/> be changed when the
54+
/// <see cref="UICulture"/> changes.
55+
/// </summary>
56+
private static bool _synchronizeThreadCulture = true;
57+
58+
#endregion
59+
60+
#region Public Interface
61+
62+
/// <summary>
63+
/// Raised when the <see cref="UICulture"/> is changed
64+
/// </summary>
65+
/// <remarks>
66+
/// Since this event is static if the client object does not detach from the event a reference
67+
/// will be maintained to the client object preventing it from being garbage collected - thus
68+
/// causing a potential memory leak.
69+
/// </remarks>
70+
public static event EventHandler UICultureChanged;
71+
72+
/// <summary>
73+
/// Sets the UICulture for the WPF application and raises the <see cref="UICultureChanged"/>
74+
/// event causing any XAML elements using the <see cref="ResxExtension"/> to automatically
75+
/// update
76+
/// </summary>
77+
public static CultureInfo UICulture
78+
{
79+
get
80+
{
81+
if (_uiCulture == null)
82+
{
83+
_uiCulture = Thread.CurrentThread.CurrentUICulture;
84+
}
85+
return _uiCulture;
86+
}
87+
set
88+
{
89+
if (value != UICulture)
90+
{
91+
_uiCulture = value;
92+
Thread.CurrentThread.CurrentUICulture = value;
93+
if (SynchronizeThreadCulture)
94+
{
95+
SetThreadCulture(value);
96+
}
97+
UICultureExtension.UpdateAllTargets();
98+
ResxExtension.UpdateAllTargets();
99+
if (UICultureChanged != null)
100+
{
101+
UICultureChanged(null, EventArgs.Empty);
102+
}
103+
}
104+
}
105+
}
106+
107+
/// <summary>
108+
/// If set to true then the <see cref="Thread.CurrentCulture"/> property is changed
109+
/// to match the current <see cref="UICulture"/>
110+
/// </summary>
111+
public static bool SynchronizeThreadCulture
112+
{
113+
get { return _synchronizeThreadCulture; }
114+
set
115+
{
116+
_synchronizeThreadCulture = value;
117+
if (value)
118+
{
119+
SetThreadCulture(UICulture);
120+
}
121+
}
122+
}
123+
124+
#endregion
125+
126+
#region Local Methods
127+
128+
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
129+
private class NOTIFYICONDATA
130+
{
131+
public int cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA));
132+
public IntPtr hWnd;
133+
public int uID;
134+
public int uFlags;
135+
public int uCallbackMessage;
136+
public IntPtr hIcon;
137+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
138+
public string szTip;
139+
public int dwState;
140+
public int dwStateMask;
141+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
142+
public string szInfo;
143+
public int uTimeoutOrVersion;
144+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
145+
public string szInfoTitle;
146+
public int dwInfoFlags;
147+
}
148+
149+
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
150+
private static extern int Shell_NotifyIcon(int message, NOTIFYICONDATA pnid);
151+
152+
/// <summary>
153+
/// Set the thread culture to the given culture
154+
/// </summary>
155+
/// <param name="value">The culture to set</param>
156+
/// <remarks>If the culture is neutral then creates a specific culture</remarks>
157+
private static void SetThreadCulture(CultureInfo value)
158+
{
159+
if (value.IsNeutralCulture)
160+
{
161+
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(value.Name);
162+
}
163+
else
164+
{
165+
Thread.CurrentThread.CurrentCulture = value;
166+
}
167+
}
168+
169+
/// <summary>
170+
/// Show the UICultureSelector to allow selection of the active UI culture
171+
/// </summary>
172+
internal static void ShowCultureNotifyIcon()
173+
{
174+
if (_notifyIcon == null)
175+
{
176+
ToolStripMenuItem menuItem;
177+
178+
_notifyIcon = new NotifyIcon();
179+
_notifyIcon.Icon = Resources.UICultureIcon;
180+
_notifyIcon.MouseClick += new MouseEventHandler(OnCultureNotifyIconMouseClick);
181+
_notifyIcon.MouseDoubleClick += new MouseEventHandler(OnCultureNotifyIconMouseDoubleClick);
182+
_notifyIcon.Text = Resources.UICultureSelectText;
183+
ContextMenuStrip menuStrip = new ContextMenuStrip();
184+
185+
// separator
186+
//
187+
menuStrip.Items.Add(new ToolStripSeparator());
188+
189+
// add menu to open culture select window
190+
//
191+
menuItem = new ToolStripMenuItem(Resources.OtherCulturesMenu);
192+
menuItem.Click += new EventHandler(OnCultureSelectMenuClick);
193+
menuStrip.Items.Add(menuItem);
194+
195+
menuStrip.Opening += OnMenuStripOpening;
196+
_notifyIcon.ContextMenuStrip = menuStrip;
197+
_notifyIcon.Visible = true;
198+
199+
// Save the window handle associated with the notify icon - note that the window
200+
// is destroyed before the ProcessExit event gets called so calling NotifyIcon.Dispose
201+
// within the ProcessExit event handler doesn't work because the window handle has been
202+
// set to zero by that stage
203+
//
204+
FieldInfo fieldInfo = typeof(NotifyIcon).GetField("window", BindingFlags.Instance | BindingFlags.NonPublic);
205+
if (fieldInfo != null)
206+
{
207+
NativeWindow iconWindow = fieldInfo.GetValue(_notifyIcon) as NativeWindow;
208+
_notifyIconHandle = iconWindow.Handle;
209+
}
210+
211+
AppDomain.CurrentDomain.ProcessExit += OnDesignerExit;
212+
}
213+
}
214+
215+
/// <summary>
216+
/// Remove the culture notify icon when the designer process exits
217+
/// </summary>
218+
/// <param name="sender"></param>
219+
/// <param name="e"></param>
220+
private static void OnDesignerExit(object sender, EventArgs e)
221+
{
222+
// By the time the ProcessExit event is called the window associated with the
223+
// notify icon has been destroyed - and a bug in the NotifyIcon class means that
224+
// the notify icon is not removed. This works around the issue by saving the
225+
// window handle when the NotifyIcon is created and then calling the Shell_NotifyIcon
226+
// method ourselves to remove the icon from the tray
227+
//
228+
if (_notifyIconHandle != IntPtr.Zero)
229+
{
230+
NOTIFYICONDATA iconData = new NOTIFYICONDATA();
231+
iconData.uCallbackMessage = 2048;
232+
iconData.uFlags = 1;
233+
iconData.hWnd = _notifyIconHandle;
234+
iconData.uID = 1;
235+
iconData.hIcon = IntPtr.Zero;
236+
iconData.szTip = null;
237+
Shell_NotifyIcon(2, iconData);
238+
}
239+
}
240+
241+
/// <summary>
242+
/// Display the CultureSelectWindow to allow the user to select the UICulture
243+
/// </summary>
244+
private static void DisplayCultureSelectWindow()
245+
{
246+
if (_cultureSelectWindow == null)
247+
{
248+
_cultureSelectWindow = new CultureSelectWindow();
249+
_cultureSelectWindow.Title = _notifyIcon.Text;
250+
_cultureSelectWindow.Closed += new EventHandler(OnCultureSelectWindowClosed);
251+
_cultureSelectWindow.Show();
252+
}
253+
}
254+
255+
/// <summary>
256+
/// Is there already an entry for the culture in the context menu
257+
/// </summary>
258+
/// <param name="culture">The culture to check</param>
259+
/// <returns>True if there is a menu</returns>
260+
private static bool CultureMenuExists(CultureInfo culture)
261+
{
262+
foreach (ToolStripItem item in _notifyIcon.ContextMenuStrip.Items)
263+
{
264+
CultureInfo itemCulture = item.Tag as CultureInfo;
265+
if (itemCulture != null && itemCulture.Name == culture.Name)
266+
{
267+
return true;
268+
}
269+
}
270+
return false;
271+
}
272+
273+
/// <summary>
274+
/// Add a menu item to the NotifyIcon for the current UICulture
275+
/// </summary>
276+
/// <param name="culture"></param>
277+
private static void AddCultureMenuItem(CultureInfo culture)
278+
{
279+
if (!CultureMenuExists(culture))
280+
{
281+
ContextMenuStrip menuStrip = _notifyIcon.ContextMenuStrip;
282+
ToolStripMenuItem menuItem = new ToolStripMenuItem(culture.DisplayName);
283+
menuItem.Checked = true;
284+
menuItem.CheckOnClick = true;
285+
menuItem.Tag = culture;
286+
menuItem.CheckedChanged += new EventHandler(OnCultureMenuCheckChanged);
287+
menuStrip.Items.Insert(menuStrip.Items.Count - 2, menuItem);
288+
}
289+
}
290+
291+
/// <summary>
292+
/// Update the notify icon menu
293+
/// </summary>
294+
private static void OnMenuStripOpening(object sender, System.ComponentModel.CancelEventArgs e)
295+
{
296+
297+
// ensure the current culture is always on the menu
298+
//
299+
AddCultureMenuItem(UICulture);
300+
301+
// Add the design time cultures
302+
//
303+
List<CultureInfo> designTimeCultures = ResxExtension.GetDesignTimeCultures();
304+
foreach (CultureInfo culture in designTimeCultures)
305+
{
306+
AddCultureMenuItem(culture);
307+
}
308+
309+
ContextMenuStrip menuStrip = _notifyIcon.ContextMenuStrip;
310+
foreach (ToolStripItem item in menuStrip.Items)
311+
{
312+
ToolStripMenuItem menuItem = item as ToolStripMenuItem;
313+
if (menuItem != null)
314+
{
315+
menuItem.Checked = (menuItem.Tag == UICulture);
316+
}
317+
}
318+
}
319+
320+
/// <summary>
321+
/// Display the context menu for left clicks (right clicks are handled automatically)
322+
/// </summary>
323+
/// <param name="sender"></param>
324+
/// <param name="e"></param>
325+
private static void OnCultureNotifyIconMouseClick(object sender, MouseEventArgs e)
326+
{
327+
if (e.Button == MouseButtons.Left)
328+
{
329+
MethodInfo methodInfo = typeof(NotifyIcon).GetMethod("ShowContextMenu",
330+
BindingFlags.Instance | BindingFlags.NonPublic);
331+
methodInfo.Invoke(_notifyIcon, null);
332+
}
333+
}
334+
335+
/// <summary>
336+
/// Display the CultureSelectWindow when the user double clicks on the NotifyIcon
337+
/// </summary>
338+
/// <param name="sender"></param>
339+
/// <param name="e"></param>
340+
private static void OnCultureNotifyIconMouseDoubleClick(object sender, MouseEventArgs e)
341+
{
342+
DisplayCultureSelectWindow();
343+
}
344+
345+
/// <summary>
346+
/// Display the CultureSelectWindow when the user selects the menu option
347+
/// </summary>
348+
/// <param name="sender"></param>
349+
/// <param name="e"></param>
350+
private static void OnCultureSelectMenuClick(object sender, EventArgs e)
351+
{
352+
DisplayCultureSelectWindow();
353+
}
354+
355+
/// <summary>
356+
/// Handle change of culture via the NotifyIcon menu
357+
/// </summary>
358+
/// <param name="sender"></param>
359+
/// <param name="e"></param>
360+
private static void OnCultureMenuCheckChanged(object sender, EventArgs e)
361+
{
362+
ToolStripMenuItem menuItem = sender as ToolStripMenuItem;
363+
if (menuItem.Checked)
364+
{
365+
UICulture = menuItem.Tag as CultureInfo;
366+
}
367+
}
368+
369+
/// <summary>
370+
/// Handle close of the culture select window
371+
/// </summary>
372+
/// <param name="sender"></param>
373+
/// <param name="e"></param>
374+
private static void OnCultureSelectWindowClosed(object sender, EventArgs e)
375+
{
376+
_cultureSelectWindow = null;
377+
}
378+
379+
#endregion
380+
381+
}
382+
383+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<Window x:Class="Infralution.Localization.Wpf.CultureSelectWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:p="clr-namespace:Infralution.Localization.Wpf" Title="Select Culture" Height="90" Width="338" MinHeight="90" MinWidth="174" Topmost="True" Icon="{p:Resx ResxName=Infralution.Localization.Wpf.Properties.Resources, Key=UICultureIcon}" ResizeMode="NoResize" MaxWidth="Infinity" MaxHeight="90">
2+
<Grid>
3+
<ComboBox Margin="12,12,12,0" Name="_cultureCombo" Height="27" VerticalAlignment="Top" SelectionChanged="_cultureCombo_SelectionChanged" DisplayMemberPath="DisplayName" />
4+
</Grid>
5+
</Window>

0 commit comments

Comments
 (0)