In my Windows Phone 8 C#/XAML .NET 4.5 App I'm using databinding from ViewModel, which is working fine.
What I'd like is for a lozalized string from LocalizedResources to be displayed as a content of a button in the following cases:
The value returned by Binding is null
The binding could not be resolved
How could this be achieved?
What I've tried to do is:
(omitted TargetNullValue, since the way to do it is probably going to be the same)
(for presentation purposes, i set the resource to be Applicationtitle)
<Button ... Content="{Binding Something, FallbackValue={Binding Path=LocalizedResources.ApplicationTitle, Source={StaticResource LocalizedStrings}}}" ... />
But what I get is text like System.Windows.Text.Data.Binding...(can't read more since it's out of screen).
Did some googling/"stackoverflowing" and found something with valueconverters for WP7, that got me a bit puzzled.
(And added C# tag because I've got a feeling this is not going to be solved just by adding the right "property" to a tag/value to a "property", although I'd appreciate it to be)
I'm pretty sure you cannot apply binding to the FallbackValue. A very simple workaround is to check for null within your 'Something' property.
private string _something;
public string Something
{
get { return _something ?? AppResources.ApplicationTitle; }
set
{
_something = value;
OnPropertyChanged("Something");
}
}
Related
I have created a custom control in UWP, but the issue I have exists on Microsoft provided custom controls as well, so I'll use the UWP Community Toolkit OrbitView as an example. The following 3 bindings work:
<Grid>
<TextBlock Text="{Binding MyProperty}"/>
<TextBlock Text="{x:Bind PageViewModel.MyProperty, Mode=OneWay}"/>
<Toolkit:OrbitView MinItemSize="{x:Bind PageViewModel.MyProperty, Mode=OneWay}" />
</Grid>
The standard control (TextBlock) works with either Binding or x:Bind. But if I want to use Binding on the custom control:
<Toolkit:OrbitView MinItemSize="{Binding MyProperty}" />
It does not. Searching here and the web I'm struggling to figure out what's going on and why. The general solution seems to be to just use x:Bind. However I want to put my custom control inside a UserControl because I want the option for it to be loaded from an external Xaml file at runtime using XamlReader. My understanding is you can't use x:Bind in this situation as that needs to be there at compile time. Is there any solution to accomplish what I'm after in UWP?
Ok, well here's why it wasn't working if you're running other code in the setter and how to get it to work.
Here's the proper way to implement a dependency property and have code execute on the setter. This was done in UWP project, since you're question is UWP, but has the exact same principles for all dependency properties of any project type.
public sealed partial class MainPage : Page
{
public MainPage() => InitializeComponent();
public int SomeValue
{
get => (int)GetValue(SomeValueProperty);
set => SetValue(SomeValueProperty, value);
}
public static readonly DependencyProperty SomeValueProperty = //Notice this is static. It's bound to an internal static hash table of some sort; I use to know exactly but forgot.
DependencyProperty.Register(
nameof(SomeValue), /*The name of the property to register against.
* The static version is always the name of the property ended with Property
* i.e. SomeValue property is SomeValueProperty dependency property */
typeof(int), //This is the type used to describe the property.
typeof(MainPage), //This is the type the dependency property relates to.
/* Below this is the magic. It's where we supply the property meta data and can be delivered different ways.
* For this example I will supply only the default value and the event we want to use when the value is changed.
* Note:
* The event we supply is fired ONLY if the value is changed. This event is what we need to use to handle changes in the setter to cover binding operations as well. */
new PropertyMetadata(default(int), /* This is the default value the dependency property will have.
* It can be whatever you decide but make sure it works with the same type or you'll most likely get an error. */
/* This is the event fired when the value changes.
* Note: Dependency properties binding and events always operate on the UI thread. Cross threading will throw exceptions. */
new PropertyChangedCallback((s, e) =>
{
var mainPage = s as MainPage; //The sender of the callback will always be of the type it's from.
/* The values given from the e argument for OldValue and NewValue should be of type int in this example...
* but since we can't gaurantee the property is setup properly before here I always add a check. */
if (e.OldValue is int oldValue) { }
if (e.NewValue is int newValue)
{
/* Now do what you want with the information. This is where you need to do custom work instead of using the setter.
* Note: If you need to work on something in the MainPage remember this is a static event and you'll need to refer to the sender or s value in this case.
* I've converted s to the variable mainPage for easy referencing here. */
mainPage.MyCustomControl.Value = newValue; //Note: The custom control should bind to this as well via XAML making this pointless. I set the value here just for educational purposes.
}
})));
}
A dependency property shouldn't look this scary, and really isn't, but I've added a lot of comments to help you navigate through it a little easier. hopefully this helps :)
No way to explain this issue except by example:
Say you have a custom UserControl with two DependencyPropertys, StatList Stats and string ImportantStat. The job of the user control is to display a table showing all of the values in Stats but with special visual treatment (like a pie chart) of the ImportantStat.
My instinct was to write a block of XAML that looked more or less like:
<PieChart Value="{Binding Path={Binding ImportantStat} }"/>
where the DataContext is prior set to Stats. So, the user passes in ImportantStat = "WinPercentage" and the pie chart binds to the WinPercentage Property of the stat line. But the user can just as easily pick some other Property to emphasize.
The problem (of course, you already know this, educated Stacker) is that you get an error message stating that you can't convert from Binding to string, which is what the outer Binding expects for Path. Though I haven't proven it to myself, I am guessing this is simply because Path is not a DependencyProperty.
So, is there any way to achieve my goal here? Feel free to break my assumptions in that first paragraph. Maybe, for example, ImportantStat can't be a string but must itself be a Binding.
Edit: Attempt #1 Failed
I was hoping that exposing from the code-behind a new DependencyProperty Binding ImportantStatBinding would allow me to rewrite the XAML as:
<PieChart Value="{Binding ImportantStatBinding, RelativeSource=... }"/>
...but to no avail. The indirect Binding is just stuck into Value itself with no attempts to resolve it.
My backup solution, which might be where this is headed, will be to just create the content inside the code-behind where I have access to ImportantStat directly and so can get away with a single Binding.
Far as I know, there is no way to concatenate data bindings in this way, without additional code. To put the problem more simply, we can have data binding (of course) of the form:
A --> B --> C
but you cannot have data binding of the form:
A --> B --> *A (*A indicates the target depends on the value of A)
because the relationships must be fixed.
It seems like it might be possible to create a Converter whose job is to convert a string into an arbitrary value by actually dereferencing a Binding using some additional context and that string as the property path. That sounds messy with type issues, so I chose the only other way I could think of:
I added a new DependencyProperty for the PieChart to the code behind and made sure that I constructed it at the appropriate times, so that the XAML could consume it. It's ugly, but it works. I just feel a little dead inside :) Hope someone finds this useful some day.
I've started creating a Windows Store App for Windows 8.1 and now I encountered a problem concerning localization.
I would like to display a string resource from a .resw file at design time, but every attempt to do so has failed, even though it works at runtime.
When using the x:Uid attribute, I still have to supply the Text property (i.e. for a TextBlock) and I don't like to write the text twice.
I also tried creating a property for the string on the viewmodel:
public string Title
{
get { return ResourceLoader.GetForCurrentView("Strings").GetString("MainView_Title"); }
}
This is working at runtime, but at design time it is blank.
So the question is, is there a way to display resources from a .resw file in the XAML-designer?
More specifically, does the ResourceManager class allow .resw files to be read at design time?
Thanks for your help,
Lucas
Old Method
So, there are a couple of things you can do.
The first (and simplest, given that you're using x:Uid already) is to just supply the text into the Text field. The x:Uid-related value will overwrite whatever is in there.
<TextBlock Text="MyText" x:Uid="MainView_Title"/>
The second method is to use the property like you already have, and then check to see if the app is in Design Time (through a couple of different methods), then return a constant value if it is and the Resource if it is not.
public string Title
{
if(ViewModelBase.IsInDesignTimeStatic) //Mvvm Light's easy accessor
return "My Text";
return ResourceLoader.GetForCurrentView("Strings").GetString("MainView_Title");
}
Hope this helps and happy coding!
Edit: There appears to be a new way to do this, at least as of Windows 8.1.
New Method
Create a class which references a ResourceLoader (similar to the property described above).
Create an indexed property accessor which accepts a string key and return the value from the ResourceLoader.
public class LocalizedStrings
{
public string this[string key]
{
get
{
return App.ResourceLoader.GetForViewIndependentUse().GetString(key);
}
}
}
In your App.xaml, define a StaticResource of this type.
<Application.Resources>
<ResourceDictionary>
<common:LocalizedStrings x:Key="Localized"/>
</ResourceDictionary>
</Application.Resources>
Now, when you want to access your property with entry key MainView_Title, use this. It's more verbose, but it should translate both in the designer and in the app itself.
<TextBlock Text="{Binding Source={StaticResource Localized}, Path=[MainView_Title]}" />
You can shuffle it around to be a bit more readable if you'd like, such as:
<TextBlock Text="{Binding [MainView_Title], Source={StaticResource Localized}}" />
This is an old thread, but since Nate provided such an elegant solution to the problem for Win8.1 I figured I'd ask here...
After much investigation and experimentation, Nate's solution does not appear to work for UWP apps for Win10 under VS2017 Community. The LocalizedString approach works just fine at runtime, but it appears
App.ResourceLoader.GetForViewIndependentUse().GetString(key);
refuses to return anything except String.Empty during design time. I've done a lot of experimenting and things like
ResourceContext.GetForViewIndependentUse().QualifierValues
Seem to be identical between runtime (working) and design time (not working).
I was wondering if anyone has encountered this and solved it. Nate? :)
Perhaps it is something trivial but I am out of ideas...
Originally I wanted to add some features to PasswordBox. Because it is a sealed class, original properties have to be replicated, among them PasswordChar. Looks trivial, but when I started to set PasswordChar in Xaml, I could not get rid of parser exception.
At the end I simply defined a new property
public char MyProperty {get; set; }
and tried to set it in Xaml as follows:
<MyPasswordBox MaxLength="3" Password="xxx" MyProperty="c" />
I am getting an exception with the call stack looking like
at MS.Internal.XcpImports.CheckHResult()
at MS.Internal.XcpImports.ConvertStringToTypedCValue()
at MS.Internal.SilverlightTypeConverter.ConvertFrom()
at MS.Internal.FrameworkCallbacks.ConvertValueToPropertyType()
....
at MS.Internal.FrameworkCallbacks.SetValueToProperty()
at MS.Internal.FrameworkCallbacks.SetPropertyAttribute()
....
at System.Windows.Application.LoadComponent()
....
As far I can read it, the type conversion string -> char fails.
Note that whenever I'll change the type of MyProperty to string (for example), everything works.
Does anybody know how to implement char properties so that they can be set from Xaml?
Working on Windows Phone 7, perhaps that's the problem. (Limited SVL 3)
I can't verify this will work, but you can give it a go. The long form xaml syntax should work ok.
Add the following to your namespace imports
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Then the following should work
<MyPasswordBox MaxLength="3" Password="xxx">
<MyPasswordBox.MyProperty>
<sys:Char>c</sys:Char>
</MyPasswordBox.MyProperty>
</MyPasswordBox>
The other solution is to look into type converters to apply to your property so that it'll convert the string for you. Type Convereters and XAML.
I have several cases where I have a property that I want to declare readonly, but also give some explanation of how it is calculated/set using the [Display(Description="")] attribute. I would like to do this in the metadata, if possible, rather than override in the dataform itself.
Here's an example:
[Display(Description = "Total number of travel hours, calculated as total hrs worked - actual working hrs this month")]
public decimal TravelHours
{
get
{
return this.TotalHrsWorked - this.ActualWorkedHours;
}
}
This won't show the description as a DescriptionViewer when I bind to this property in a DataForm & DataField.
It seems like when I set the [ReadOnly] attribute it hides the DescriptionViewer, and even setting DescriptionViewerVisibility=Visible in the dataform xaml still doesn't change it. Also, any calculated properties (no setter) seem to have this attribute enforced by default. It's kind of annoying, because these are the ones I really want to show the descriptionviewer for.
The only way around it I have found so far is to make the property not readonly and add a dummy setter (for calculated properties). That seems like a kludge.
Is there any way to show the dataform/datafield descriptionviewer on readonly properties?
Yeah,
I encountered same problem before but didn't try to manage.
Display Attribute and , ReadOnly Attribute's are sealed you can't inherit from them. You may wrap them and create another attribute but your dataform doesn't now this attribute.So you can't...
Maybe you can do different things in ReadOnlyTemplate
<dataFormToolkit:DataForm.ReadOnlyTemplate>
<DataTemplate>
<Grid>
<dataFormToolkit:DataField Label="{Binding ReadOnlyLabel,
Converter=ReadOnlyOrNotConverter}" >
<TextBox Text="{Binding Path=ReadOnlyValueEtc, Mode=TwoWay}" />
</dataFormToolkit:DataField>
Hope helps,
Regards!
[Display(Order = 6, Name = "CountryLabel", Description = "CountryDescription",
ResourceType = typeof(EntityDataStrings))]
Where EntityDataStrings is a resource file containing values for "CountryLabel" and "CountryDescription".