|
|
Hi, I am using this extension, and I must say it's much better than LocBaml I tried earlier :) But there is one thing I need and it is not implemented.
Since I am using only one dictionary for all the translations in application i need to have an option to declare default assembly and default dictionary, so I could type only key in xaml and value will be found :) So i made following changes to sourcecode:
in LocalizeDictionary.cs:
I added properties for defaults
/// <summary>
/// Holds default assembly name
/// </summary>
private static string defaultAssembly = "";
/// <summary>
/// Gets / Sets default assembly
/// </summary>
public static string DefaultAssembly
{
get
{
return !String.IsNullOrEmpty(defaultAssembly) ?
defaultAssembly :
Instance.GetAssemblyName(System.Reflection.Assembly.GetExecutingAssembly());
}
set
{
defaultAssembly = value;
}
}
/// <summary>
/// Holds default dictionary name
/// </summary>
private static string defaultDict = "";
/// <summary>
/// Gets / Sets default dictionary
/// </summary>
public static string DefaultDict
{
get
{
return !String.IsNullOrEmpty(defaultDict) ? defaultDict : ResourcesName;
}
set
{
defaultDict = value;
}
}
Changed ParseKey to:
/// <summary>
/// Parses a key ([[Assembly:]Dict:]Key and return the parts of it.
/// </summary>
/// <param name="inKey">The key to parse.</param>
/// <param name="outAssembly">The found or default assembly.</param>
/// <param name="outDict">The found or default dictionary.</param>
/// <param name="outKey">The found or default key.</param>
public static void ParseKey(string inKey, out string outAssembly, out string outDict, out string outKey)
{
// reset the vars to null
outAssembly = null;
outDict = null;
outKey = null;
// its a assembly/dict/key pair
if (!string.IsNullOrEmpty(inKey))
{
string[] split = inKey.Trim().Split(":".ToCharArray(), 3);
// assembly:dict:key
if (split.Length == 3)
{
outAssembly = !string.IsNullOrEmpty(split[0]) ? split[0] : null;
outDict = !string.IsNullOrEmpty(split[1]) ? split[1] : null;
outKey = split[2];
}
// dict:key
// assembly = ExecutingAssembly
if (split.Length == 2)
{
outAssembly = !String.IsNullOrEmpty(DefaultAssembly) ? DefaultAssembly : null;
outDict = !string.IsNullOrEmpty(split[0]) ? split[0] : null;
outKey = split[1];
}
// key
// assembly = ExecutingAssembly
// dict = standard resourcedictionary
if (split.Length == 1)
{
outAssembly = !String.IsNullOrEmpty(DefaultAssembly) ? DefaultAssembly : null;
outDict = !String.IsNullOrEmpty(DefaultDict) ? DefaultDict : null;
outKey = split[0];
}
}
else
{
// if the passed value is null pr empty, throw an exception if in runtime
if (!Instance.GetIsInDesignMode())
{
throw new ArgumentNullException("inKey");
}
}
}
in BaseLocalizeExtension.cs :
changed propetries
/// <summary>
/// Gets or sets the name of the Assembly where the .resx is located.
/// If it's null, the default assembly (where this LocalizeEngine is located at) will get returned
/// </summary>
public string Assembly
{
get
{
return this.assembly ??
LocalizeDictionary.DefaultAssembly;
}
set { this.assembly = !string.IsNullOrEmpty(value) ? value : null; }
}
/// <summary>
/// Gets or sets the Name of the .resx dictionary.
/// If it's null, default will get returned
/// </summary>
public string Dict
{
get { return this.dict ?? LocalizeDictionary.DefaultDict; }
set { this.dict = !string.IsNullOrEmpty(value) ? value : null; }
}
And it works for me. I just specify default values on application startup:
WPFLocalizeExtension.Engine.LocalizeDictionary.DefaultAssembly = "DefaultAsembly";
WPFLocalizeExtension.Engine.LocalizeDictionary.DefaultDict = "DefaultDictionary";
and I can get localized values just by key:
<TextBlock Name="TextName" Text="{loc:LocText myTextKey}"/>
So my question is: Is that all, or do I have to change something else? I don't know that much about this source code and I don't want to get errors I did not know about.
I can send you source code by email, I'm new at CodePlex and don't know if I can share here scode on hosting like Mediafire etc.
|
|
Coordinator
Jan 13, 2012 at 11:09 AM
|
Hi uukasz,
your code looks correct!
Just one thing bugs me with the default values: if a coder do not know that he MAY set the default values just a single time (eg. app startup), the extension has a strange behaviour if it will be set afterwards during runtime.
please introduce an InitializeDefaultValues method that checks if it was called only once and change the properties like this:
private static string defaultAssembly = "";
to
public static string DefaultAssembly {get; private set;}
with this change i probably would implement this change and it would help many coders :)
To bring the your code changes into the source please Fork my GitHub project, change the source and send me a pull-request.
https://github.com/SeriousM/WPFLocalizationExtension
Thanks!
|
|
Coordinator
Jan 19, 2012 at 8:02 AM
|
Hi all,
I liked the idea of default settings for the assembly and dictionary but would like to make the following proposal: Why not using dependency properties, to allow us changing the defaults freely throuout the visual tree?
Refer to my fork on GitHub under
https://github.com/MrCircuit/WPFLocalizationExtension
I've implemented this leading to a nice XAML code like this:
<Window x:Class="AssemblyTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Engine="clr-namespace:WPFLocalizeExtension.Engine;assembly=WPFLocalizeExtension"
Engine:LocalizeDictionary.DesignCulture="de"
Engine:LocalizeDictionary.DefaultAssembly="AssemblyTest"
Engine:LocalizeDictionary.DefaultDictionary="Strings"
xmlns:lex="clr-namespace:WPFLocalizeExtension.Extensions;assembly=WPFLocalizeExtension"
Title="MainWindow" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="114" d:DesignWidth="248" SizeToContent="WidthAndHeight">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="{lex:LocText TestText}" Height="28" HorizontalAlignment="Center"/>
<StackPanel Name="TestPanel" Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" Engine:LocalizeDictionary.DefaultDictionary="Strings2">
<Button Content="{lex:LocText ButtonDE}" Height="23" Width="75" Click="ButtonDE_Click" Engine:LocalizeDictionary.DefaultAssembly="AssemblyTestResourceLib" />
<Button Content="{lex:LocText ButtonEN}" Height="23" Width="75" Click="ButtonEN_Click" />
</StackPanel>
<Button HorizontalAlignment="Center" Name="ButtonAssembly" Content="Assembly" Grid.Row="2" Height="23" Width="75" Click="ButtonAssembly_Click" />
</Grid>
</Window>
Best,
Uwe
|
|
Coordinator
Jan 20, 2012 at 2:34 PM
|
Hi Uwe,
this could work, yes.
does this work well with data templates?
best regards,
bernhard
|
|
Coordinator
Jan 23, 2012 at 6:56 AM
|
After small adaptations now, yes ;-)
|
|
|
|
Hi,
I took the source code from
https://github.com/MrCircuit/WPFLocalizationExtension and built it. I had to include the XAMLMarkupExtensions in my project now to continue to use the WPFLocalizeExtension. Is this correct, or am I building it incorrectly?
Assuming that I do need to include the new reference, everything seems to work...except the designer in VS2010 won't load anymore, and I get this exception and stack trace. I do not reference the XAMLMarkupExtensions assembly in my XAML, but it was required
to get the project to compile. It does run correctly, I just have this designer issue...
Any thoughts?
-Dan
at System.Reflection.Adds.AssemblyProxy.GetResolvedAssembly() at System.Reflection.Adds.AssemblyProxy.GetHashCode() at Microsoft.Windows.Design.Metadata.ReflectionMetadataContext.Reflectable`1.GetHashCode() at System.Collections.Generic.GenericEqualityComparer`1.GetHashCode(T
obj) at System.Collections.Generic.HashSet`1.InternalGetHashCode(T item) at System.Collections.Generic.HashSet`1.Contains(T item) at Microsoft.Windows.Design.Metadata.ReflectionMetadataContext.CanParentContextSupportType(Reflectable`1 type) at Microsoft.Windows.Design.Metadata.ReflectionMetadataContext.GetTypeForReflectableType(Reflectable`1
reflectableType) at MS.Internal.Metadata.ClrType.GetTypeMetadata(Type type) at MS.Internal.Metadata.ClrType.get_BaseType() at MS.Internal.Design.Metadata.Xaml.XamlType.<GetAllAttachableProperties>d__7.MoveNext() at MS.Internal.Design.Metadata.Xaml.XamlType.<FindAttachableProperties>d__0.MoveNext()
at Microsoft.Windows.Design.Metadata.Xaml.XamlExtensionImplementations.<FindAttachableProperties>d__7.MoveNext() at MS.Internal.VirtualModel.VirtualModelPropertyCollection.<GetUncachedProperties>d__0.MoveNext() at System.Linq.Buffer`1..ctor(IEnumerable`1
source) at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source) at MS.Internal.VirtualModel.VirtualModelPropertyCollection.GetEnumerator() at MS.Internal.VirtualModel.VirtualModelUtilities.WrapProperties(IVirtualModelItem item) at MS.Internal.VirtualModel.VirtualModelItem.System.ComponentModel.ICustomTypeDescriptor.GetProperties()
at System.ComponentModel.TypeDescriptor.MergedTypeDescriptor.System.ComponentModel.ICustomTypeDescriptor.GetProperties() at System.ComponentModel.TypeDescriptor.GetPropertiesImpl(Object component, Attribute[] attributes, Boolean noCustomTypeDesc, Boolean noAttributes)
at System.Windows.PropertyPath.ResolvePropertyName(String name, Object item, Type ownerType, Object context, Boolean throwOnError) at MS.Internal.Data.PropertyPathWorker.GetInfo(Int32 k, Object item, SourceValueState& svs) at MS.Internal.Data.PropertyPathWorker.ReplaceItem(Int32
k, Object newO, Object parent) at MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(Int32 k, ICollectionView collectionView, Object newValue, Boolean isASubPropertyChange) at MS.Internal.Data.ClrBindingWorker.AttachDataItem() at System.Windows.Data.BindingExpression.Activate(Object
item) at System.Windows.Data.BindingExpression.AttachToContext(AttachAttempt attempt) at System.Windows.Data.BindingExpression.AttachOverride(DependencyObject target, DependencyProperty dp) at System.Windows.Data.MultiBindingExpression.AttachBindingExpression(Int32
i, Boolean replaceExisting) at System.Windows.Data.MultiBindingExpression.AttachOverride(DependencyObject d, DependencyProperty dp) at System.Windows.Data.BindingExpressionBase.Attach(DependencyObject target, DependencyProperty dp) at System.Windows.Data.BindingExpressionBase.OnAttach(DependencyObject
d, DependencyProperty dp) at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
at System.Windows.Data.BindingOperations.SetBinding(DependencyObject target, DependencyProperty dp, BindingBase binding) at MS.Internal.GridControl.GridRowAdornerBase..ctor(ModelItem gridItem, ModelItem rowItem) at MS.Internal.GridControl.GridRowSelectAdorner..ctor(ModelItem
grid, ModelItem row) at MS.Internal.GridControl.GridDesigner.EnsureRowAdorners(ModelItem rowDefModel, Boolean withHandle) at MS.Internal.GridControl.GridDesigner.EnsureAllRowAdorners() at MS.Internal.GridControl.GridDesigner.InitGridDesigner() at MS.Internal.GridControl.GridDesigner.GetDesigner(ModelItem
gridItem) at MS.Internal.GridControl.AlwaysVisibleGridAdornerProvider.Activate(ModelItem item) at Microsoft.Windows.Design.Interaction.AdornerProvider.InvokeActivate(EditingContext context, ModelItem item) at MS.Internal.Features.AdornerProviderFeatureConnector.FeatureProvidersAdded(ModelItem
item, IEnumerable`1 extensions) at Microsoft.Windows.Design.Policies.PolicyDrivenFeatureConnector`1.UpdateFeatureProviders() at MS.Internal.Features.PolicyDrivenToolFeatureConnector`1.UpdateCurrentTool(Tool newTool) at MS.Internal.Features.PolicyDrivenToolFeatureConnector`1.<.ctor>b__0(Tool
newTool) at Microsoft.Windows.Design.ContextItemManager.SubscribeProxy`1.SubscribeContext(ContextItem item) at Microsoft.Windows.Design.SubscribeContextCallback.Invoke(ContextItem item) at Microsoft.Windows.Design.EditingContext.DefaultContextItemManager.OnItemChanged(ContextItem
item) at Microsoft.Windows.Design.EditingContext.DefaultContextItemManager.SetValue(ContextItem value) at MS.Internal.Host.ToolSubsystem.ActivateTool(ToolFactory toolFactory) at MS.Internal.Host.ToolSubsystem..ctor(EditingContext editingContext, DesignerContext
designerContext) at MS.Internal.Host.Designer.Load() at MS.Internal.Designer.VSDesigner.Load() at MS.Internal.Designer.VSIsolatedDesigner.VSIsolatedView.Load() at MS.Internal.Designer.VSIsolatedDesigner.VSIsolatedDesignerFactory.Load(IsolatedView view) at
MS.Internal.Host.Isolation.IsolatedDesigner.BootstrapProxy.LoadDesigner(IsolatedDesignerFactory factory, IsolatedView view) at MS.Internal.Host.Isolation.IsolatedDesigner.BootstrapProxy.LoadDesigner(IsolatedDesignerFactory factory, IsolatedView view) at MS.Internal.Host.Isolation.IsolatedDesigner.Load()
at MS.Internal.Designer.DesignerPane.LoadDesignerView(Boolean isReload)
System.Reflection.Adds.UnresolvedAssemblyException
Type universe cannot resolve assembly: XAMLMarkupExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
|
|
Coordinator
Mar 16, 2012 at 3:41 PM
|
You are right - you need the XAMLMarkupExtensions library. I suggest to include this library with the nuget-package that comes along with the XAMLMarkupExtensions project. It should all work then properly.
Best regards,
Uwe
|
|
Coordinator
Mar 16, 2012 at 3:46 PM
|
This discussion has been copied to a work item. Click
here to go to the work item and continue the discussion.
|
|
|
|
Yes, you need a reference to the XAMLMarkupExtensions library in your project just to get it to compile. I have that, and the application RUNS fine.
But even though it compiles and runs fine, the designer in VS2010 gives me the above exception and will not load. Do I need to add something to the XAML to help this go away?
-Dan
|
|
Coordinator
Mar 16, 2012 at 4:27 PM
|
The XAML code needs no extra reference to this libtrary.
Could you please paste the particular XAML that crashes? I will try to resolve this issue.
|
|
|
|
Here's the file. I'm just doing a proof-of-concept at this point, to see how easy it is to swap languages on the fly. I really like the ability to set the default assembly and dictionary, so the "binding" isn't as cluttered.
<Window x:Class="LanguageTester.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:localizeEngine="clr-namespace:WPFLocalizeExtension.Engine;assembly=WPFLocalizeExtension"
xmlns:localExt="clr-namespace:WPFLocalizeExtension.Extensions;assembly=WPFLocalizeExtension"
localizeEngine:LocalizeDictionary.DesignCulture="en"
localizeEngine:LocalizeDictionary.DefaultAssembly="LanguageTester"
localizeEngine:LocalizeDictionary.DefaultDictionary="Strings"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button Grid.Row="0"
Content="{localExt:LocText Button}" />
<TextBlock Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{localExt:LocText TextBlock}" />
<Label Grid.Row="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{localExt:LocText Label}" />
<GroupBox Grid.Row="3"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Header="{localExt:LocText GroupBox}" />
<CheckBox Grid.Row="4"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{localExt:LocText CheckBox}" />
<RadioButton Grid.Row="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{localExt:LocText RadioButton}" />
<ComboBox x:Name="myComboBox"
Grid.Row="6"
HorizontalAlignment="Center"
VerticalAlignment="Center"
MinWidth="150" />
<Button Grid.Row="7"
Content="Change Language"
Click="Button_Click" />
</Grid>
</Window>
-Dan
|
|
Coordinator
Mar 16, 2012 at 4:53 PM
|
I'm sorry, but your code looks well and runs perfectly in my Visual Studio. Therefore I would like to ask you to send me the solution via pm?
|
|
|
|
Hi,
Okay, nevermind. I went to check on the DLL in Windows, and it was marked as "blocked", since it wasn't compiled locally and was actually included in the ZIP that I downloaded from codeplex.
After unblocking, I no longer get the error.
Sorry, and thanks for the helpful info!
-Dan
|
|
|
|
I just stumbled across this thread and wonder what happened to the automatic assembly detection?
Using reflection, we could avoid magic strings in our XAML that break when we rename an assembly.
It could also fix some problems where the attached properties can not be found because a parent property is missing (GridViewColumns etc.).
|
|