I'm trying to pass my current culture (that has a custom decimal symbol) to WPF, so that it will display bound values according to my region and language settings in windows.
My researches always ended up with the a solution similar to this, which passes the language tag, but not any additional settings (like the decimal symbol).
How can I force WPF to use the whole current culture and not only the default language settings?
Questions about possible a possible workaround:
Can I somehow pass the current culture to the default value converters used by WPF? Or maybe override them?
There's couple options. Maybe the easiest one is to wrap the values you want to databind to screen and call ToString for them. For example, if you have:
public decimal Value
{
get { return this.value; }
set
{
if (value == this.value) return;
this.value = value;
OnPropertyChanged();
}
}
Wrap it inside your ViewModel like this:
public decimal Value
{
get { return this.value; }
set
{
if (value == this.value) return;
this.value = value;
OnPropertyChanged("ValueString");
}
}
public string ValueString
{
get { return this.value.ToString(CultureInfo.CurrentCulture); }
}
And bind your UI against this new property:
<TextBlock x:Name="Result" Text="{Binding ValueString}" Grid.Row="0"/>
This way you will automatically get the formatting based on your computer's culture settings:
Another alternative is to use the method presented in here: https://stackoverflow.com/a/19796279/66988
So you need a custom Binding class:
public class CultureAwareBinding : Binding
{
public CultureAwareBinding(string path)
: base(path)
{
ConverterCulture = CultureInfo.CurrentCulture;
}
}
And then you have to use that in your XAML:
<TextBlock x:Name="Result" Text="{wpfApplication9:CultureAwareBinding Value}" Grid.Row="0"/>
After which you should see the desired output:
using System;
using System.Globalization;
using System.Threading;
using System.Windows;
using System.Windows.Markup;
namespace WPF_CultureExample
{
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("tr-TR");
var currentCulture = Thread.CurrentThread.CurrentCulture.Name;
var ci = new CultureInfo(currentCulture)
{
NumberFormat = { NumberDecimalSeparator = "," },
DateTimeFormat = { DateSeparator = "." }
};
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
base.OnStartup(e);
}
}
}
You can recode the OnStartup() method in the backend codes of the App.xaml file.
Related
I have an username label and need to view this as uppercase but this should only relate to the UI. The data (string) should be saved in the db as actual case whatever it is. Could anyone tell me if there is anyway to convert it to uppercase without doing so through the code behind?
You can use Label.TextTransform with TextTransform.Uppercase.
XAML
<Label TextTransform="Uppercase" />
C#
var label = new Label
{
TextTransform = TextTransform.Uppercase
};
As you're aware you can do this from the code behind as such:
string data = "my data";
UILabel myLabel = new UILabel();
myLabel.Text = data.ToUpper();
So bearing in mind that you don't want to do it this way you would need to derive from UILabel and create your own, then simply add the ToUpper() onto the end of the get;set; values of the Text property.
using CoreGraphics;
using System;
using UIKit;
namespace MyApp.Controls
{
partial class Control_UpperLabel : UILabel
{
public Control_UpperLabel IntPtr handle) : base(handle)
{
//
}
public Control_UpperLabel()
{
//
}
public override void Draw(CGRect rect)
{
base.Draw(rect);
}
public override string Text { get => base.Text.ToUpper(); set => base.Text = value.ToUpper(); }
}
}
EDIT: As per comments below, here is an alternative solution for Xamarin.Forms
This uses a value converter as part of a binding solution. It's also been slightly amended to use the suggestion by clint in the comments below. Thanks.
public class StringCaseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
switch ((parameter as string).ToUpper()[0])
{
case 'U':
return ((string)value).ToUpper();
case 'L':
return ((string)value).ToLower();
default:
return ((string)value);
};
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
It would be used in the XAML as such:
Text="{Binding Text, Converter={StaticResource caseConverter}, ConverterParameter=u}}"
Or you can use Bindable property then format the text on the getter :
e.g.:
public static readonly BindableProperty ItemLabelProperty =
BindableProperty.Create(nameof(ItemLabel), typeof(string),
typeof(DetailsLineItemControl), default(string), BindingMode.OneWay);
public string ItemLabel
{
get
{
var value = (string)GetValue(ItemLabelProperty);
return !string.IsNullOrEmpty(value) ? value.ToUpper() : value;
}
set
{
SetValue(ItemLabelProperty, value);
}
}
I want to bind the selected Calender View Item and set it to
a DateTime Variable.
My CalenderView Xaml looks like:
<CalendarView Grid.Row="6" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20"/>
I have an DateTime item in the Datacontext class:
private DateTime _DueDate;
public DateTime DueDate
{
get { return this._DueDate; }
set
{
if (this._DueDate != value)
{
this._DueDate = value;
base.PropertyOnChanged("DueDate");
}
}
}
And the DateTimeConverter:
public class DateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
DateTime date = ((DateTime)value);
return date.Day + "." + date.Month + "." + date.Year;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return DateTime.Parse((string)value);
}
}
Here is also the Doc to the Calender View:
CalenderView MSDN
In the Docs is a Property SelectedDate, but I only see in the XAML SelectedDateChanged EventHandler. But I want to do it in MVVM.
My Problem is I don´t know on which Property I can set the
Binding. I looked in the Doc but I only find the Date="" property
from the DatePicker but I don´t find anything to the CalenderView.
UPDATE
Following to the Comment from
#Juo Zuo:"CalendarView has a SelectedDates property. Usually, we can use this property to set the selected date like: MyCalendarView.SelectedDates.Add(new DateTime(2016, 5, 5));. However this property is read-only, we can't use it for binding. So, I'm afraid there is no way to set selected dates with Binding"
I would expand the Question.
My Question is:
Is there any way to use the Calender View with the MVVM Pattern from MSDN ?
All you need to do is to create an attached property and encapsulate the SelectedDates.Add logic within it.
public static class CalendarViewHelper
{
public static IList<DateTimeOffset> GetSelectedDates(DependencyObject obj)
{
return (IList<DateTimeOffset>)obj.GetValue(SelectedDatesProperty);
}
public static void SetSelectedDates(DependencyObject obj, IList<DateTimeOffset> value)
{
obj.SetValue(SelectedDatesProperty, value);
}
public static readonly DependencyProperty SelectedDatesProperty =
DependencyProperty.RegisterAttached("SelectedDates", typeof(IList<DateTimeOffset>), typeof(CalendarView),
new PropertyMetadata(null, (d, e) =>
{
var cv = d as CalendarView;
var dates = e.NewValue as IList<DateTimeOffset>;
if (cv != null && dates != null)
{
foreach (var date in dates)
{
cv.SelectedDates.Add(date);
}
}
}));
}
<CalendarView local:CalendarViewHelper.SelectedDates="{x:Bind Dates, Mode=OneWay}" />
If your Dates property has more than one items inside, make sure you change the SelectionMode to Multiple.
I'm building a windows8.1 app and trying to tamper with the ConventionManager of caliburn.micro . I want to enable name convention binding with ValueConverters.
More specifically I have a collection of ProductViewModels that have two properties Value and IsValid. I want to bind the Value property to a textblock and the IsValid property to the BorderColorBrush of a Border via a ValueConverter.
Here's my code inside Configure in app.xaml.cs
var oldApplyConverterFunc = ConventionManager.ApplyValueConverter;
ConventionManager.ApplyValueConverter = (binding, bindableProperty, property) =>
{
if (bindableProperty == Control.BorderBrushProperty && typeof(bool).IsAssignableFrom(property.PropertyType))
// ^^^^^^^ ^^^^^^
// Property in XAML Property in view-model
binding.Converter = booleanToBrush;
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// My converter used here. Code never reaches this point
// else I use the default converter
else if (bindableProperty == TextBlock.TextProperty && typeof(int).IsAssignableFrom(property.PropertyType))
{
//Code reaches this point when it should.
oldApplyConverterFunc(binding, bindableProperty, property);
}
else
{
oldApplyConverterFunc(binding, bindableProperty, property);
}
};
The problem is that the program never enters the first if clause. Does anybody know why???
My VM and xaml are these.
public class ProductViewModel
{
public ProductViewModel(int value)
{
_value = value;
}
private int _value;
public int Value
{
get { return _value; }
set
{
_value = value;
}
}
private bool _isValid;
public bool IsValid
{
get
{
_isValid = some validation logic;
return _isValid;
}
}
}
the view is a user control
<Border x:Name="IsValid">
<TextBlock x:Name="Value"/>
</Border>
I needed to make my own label to hold some value, that is diferent from the value displayed to user
public class LabelBean : Label {
private string value;
public LabelBean(string text = "", string value = ""): base() {
base.Text = text;
this.value = value;
}
public string Value {
get { return value; }
set { this.value = value; }
}
}
but now id in the form constructor I replace the control with my class
this.lbAttributeType = new LabelBean();
and later after the form is created, but before it is shown I set the text through setter
(this.lbAttributeType as LabelBean).Value = value;
this.lbAttributeType.Text = Transform(value);
but in the form I have always "label1" text... what is wrong with it?
thanks
UPDATE
I added the solution here to find it easier:
public class MyLabel : Label {
public MyLabel()
: base() {
}
public string Value {
set {
this.Text = value;
}
}
}
and the form with Widnows.Forms.Label label1 control
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.Controls.Remove(this.label1);
this.label1 = new MyLabel();
this.Controls.Add(this.label1);
(this.label1 as MyLabel).Value = "oh";
}
}
the bug was in the Controls.Remove and Controls.Add,
thanks all for their time :)
My guess is because, since you're doing the work in the constructor, the InitializeComponent code, automatically generated by the designer, is overwriting the control instance, as it's most likely called after your initialisation.
If the class is part of the project, you will find it on the toolbox; meaning you can simply drag and drop your new control on the form in place of the existing one - this is what you should do.
This ensures that the designer-generated property is of your LabelBean type, and not simply Label.
Also - you should consider changing your Value setter as demonstrated by WoLfulus (+1 there)
Update
In response to the comment you put on WoLfulus' answer - here's a couple of alternatives:
1) If the form is the 'clever' bit here - consider just writing a helper method in it, and setting the value of the label through it, leveraging the Tag property:
public void SetLabelBean(Label target, string value)
{
Label.Tag = value;
Label.Text = Transform(value);
}
public string GetLabelBean(Label target)
{
return target.Tag as string;
}
2) Continue using your sub-classed LabelBean type (adding it via the designer as I've already mentioned) - but use an abstraction to give it access to the form's Transform method:
public interface ITransformProvider
{
string Transform(string);
}
Make your form class implement this interface, with the Transform method you elude to.
Now, in your LabelBean class:
public ITransformProvider Transformer
{
get{
//searches up the control hierarchy to find the first ITransformProvider.
//should be the form, but also allows you to use your own container controls
//to change within the form. The algorithm could be improved by caching the
//result, invalidating it if the control is moved to another container of course.
var parent = Parent;
ITransformProvider provider = parent as ITransformProvider;
while(provider == null){
parent = parent.Parent;
provider = parent as ITransformProvider;
}
return provider;
}
}
And then, finally, using WoLfulus' code, but slightly changed, you can do this:
public string Value
{
get
{
return value;
}
set
{
this.value = value;
var transformer = Transformer;
if(transformer != null) this.Text = transformer.Transform(value);
}
}
That, I think, addresses your issues with that answer.
Try this:
Create a new delegate outside the label class:
public delegate string LabelFormatDelegate( string val );
Add this to your label class:
public LabelFormatDelegate ValueFormatter = null;
public string Value
{
get
{
return value;
}
set
{
this.value = value;
if (this.ValueFormatter != null)
{
this.Text = this.ValueFormatter(value); // change the label here
}
else
{
this.Text = value;
}
}
}
Place a new common label to your form (lets name it "label1")
Goto to Form1.Designer.cs and search for "label1" declaration.
Rename the "Label" type to your own label type (Ex: "MyLabel")
Change the initialization code of label on InitializeComponent function on designer code to match the new type "MyLabel"
Example:
this.label1 = new Label();
Change to:
this.label1 = new MyLabel();
In the Form_Load event, specify the format function:
this.label1.ValueFormatter = new LabelFormatDelegate(this.Transform);
Notes: You'll need to remove the "Text" setter call too from here:
(this.lbAttributeType as LabelBean).Value = value;
// this.lbAttributeType.Text = Transform(value);
This will keep your value/text in sync but remember not to set "Text" property by hand.
I agree with WoLfulus and Andreas Zoltan and would add a symmetrical functionality to Text if there exists a unambiguous reverse transformation:
public string Value
{
get { return value; }
set
{
if (this.value != value) {
this.value = value;
this.Text = Transform(value);
}
}
}
public override string Text
{
get { return base.Text; }
set
{
if (base.Text != value) {
base.Text = value;
this.value = TransformBack(value);
}
}
}
Note the if checks in order to avoid an endless recursion.
EDIT:
Assigning your label to lbAttributeType is not enough. You must remove the old label from the Controls collection before the assignment and re-add it after the assignment.
this.Controls.Remove(lbAttributeType); // Remove old label
this.lbAttributeType = new LabelBean();
this.Controls.Add(lbAttributeType); // Add new label
Your form was still displaying the old label! Why did I not see it earlier?
When using IDataErrorInfo in WPF is there a way to pass parameters to the validator. For instance I have a DueDate Datepicker. When validating for a new task I want to restrict the date allowed to today or later but when editing I need to allow for DueDates before today since a task can be edited that is past due.
My DatePicker in Xaml (.Net 4.0)
<DatePicker SelectedDate="{Binding Path=SelectedIssue.IssDueDate,
ValidatesOnDataErrors=True}" />
My IErrorDataInfo
namespace OITaskManager.Model
{
public partial class Issue : IDataErrorInfo
{
// I want to set these values from the Xaml
public DateTime minDate = new DateTime(2009, 1, 1);
public DateTime maxDate = new DateTime(2025, 12, 31);
public string this[string columnName]
{
get
{
if (columnName == "IssDueDate")
{
if (IssDueDate < minDate || IssDueDate > maxDate)
{
return "Due Date must be later than " + minDate.Date +
" and earlier than " + maxDate.Date;
}
return null;
}
return null;
}
}
You could just use a custom validator on the binding. Or you could maintain a IsNew internal state on the the Issue object instance until it is no longer considered new.