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.
Hello I have a DataGrid and I have different reports that I want to show. I'm going to change the classes so they are shorter in here but Idea is the same.
Lets say that I Have an Interface called IReports
public interface IReports
{
}
and three classes called Students, Classes, Cars
public class Students:IReports
{
public string Name { get; set; }
}
public class Classes : IReports
{
public string ClassName { get; set; }
public string StudentName { get; set; }
}
public class Cars : IReports
{
public int Mileage { get; set; }
public string CarType { get; set; }
public string StudentName { get; set; }
}
The List
private List<IReports> _reportsTable;
public List<IReports> ReportsTable
{
get { return _reportsTable; }
set { SetProperty(ref (_reportsTable), value); }
}
the DataGrid
<DataGrid ItemsSource="{Binding ReportsList}"
Grid.Column="1"
Grid.Row="0"
AutoGenerateColumns="True"
Grid.RowSpan="6"/>
Okay, so what is important here is they all have different property names and some have more some have less. How can I bind the DataGrid to look at the different properties? This is MVVM if that makes any difference.
Update: What this will always only use one of the classes at a time.but when someone changes a combobox it will fire an event that will fill the IList<IReports>.
What this will always only use one of the classes at a time. but when someone changes a combobox it will fire an event that will fill the IList<IReports>.
The way I understand the above is that you never mix different elements inside the list (i.e. it contains only Classes, Students or Cars). All the other answers are assuming the list contains mixed content, but if that's the true, then DataGrid is simply not the right presenter for such content.
If the above assumption is correct, then the only problem is how to represent different lists with a single bindable property. As can be seen in Data Binding Overview, when dealing with collection, data binding does not really care if they are generic or not. The recognizable source types are the non generic IEnumerable, IList and IBindingList. However, the collection view implementation is using some rules to determine the element type of the collection, by seeking for generic type argument of implemented IEnumerable<T> interfaces by the actual data source class, by checking the first available item, or taking the information from ITypedList implementation etc. All the rules and their precedence can be seen in the Reference Source.
With all that in mind, one possible solution could be to change the ReportsTable property type to allow assigning List<Classes> or List<Students or List<Cars>. Any common class/interface will work (remember, data binding will check the actual type returned by GetType()) like object, IEnumerable, IList, IEnumerable<IReports> etc., so I'll choose the closest covariant type to List<IReports which is IReadOnlyList<IReports>:
private IReadOnlyList<IReports> _reportsTable;
public IReadOnlyList<IReports> ReportsTable
{
get { return _reportsTable; }
set { SetProperty(ref (_reportsTable), value); }
}
Now when you do this
viewModel.ReportsTable = new List<Students>
{
new Students { Name = "A" },
new Students { Name = "B" },
new Students { Name = "C" },
new Students { Name = "D" },
};
you get
while with this
viewModel.ReportsTable = new List<Classes>
{
new Classes { ClassName = "A", StudentName = "A" },
new Classes { ClassName = "A", StudentName ="B" },
new Classes { ClassName = "B", StudentName = "C" },
new Classes { ClassName = "B", StudentName = "D" },
};
it shows
and finally this
viewModel.ReportsTable = new List<Cars>
{
new Cars { Mileage = 100, CarType = "BMW", StudentName = "A" },
new Cars { Mileage = 200, CarType = "BMW", StudentName = "B" },
new Cars { Mileage = 300, CarType = "BMW", StudentName = "C" },
new Cars { Mileage = 400, CarType = "BMW", StudentName = "D" },
};
results in
UPDATE: The above requires modifying the model to return concrete List<T> instances. If you want to keep the model as it is (i.e. returning List<IReports>), then you'll need a different solution, this time utilizing the ITypedList. In order to do that, we'll create a simple list wrapper using the System.Collections.ObjectModel.Collection<T> base class:
public class ReportsList : Collection<IReports>, ITypedList
{
public ReportsList(IList<IReports> source) : base(source) { }
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
return TypeDescriptor.GetProperties(Count > 0 ? this[0].GetType() : typeof(IReports));
}
public string GetListName(PropertyDescriptor[] listAccessors) { return null; }
}
then change the bindable property to
private IList<IReports> _reportsTable;
public IList<IReports> ReportsTable
{
get { return _reportsTable; }
set { SetProperty(ref _reportsTable, value as ReportsList ?? new ReportsList(value)); }
}
and you are done.
As I understand it, you want a datagrid to show the various columns of various classes that implement an interface. If you hook the DataGrid's LoadingRow event, you can see what types of objects you are dealing with at runtime. You can use reflection to get the properties off the row's datacontext and then check the datagrid to see if there is a column for that property. If not, add it.
An issue will be if there are different types in the list and a type doesn't have a property that is in another type (like Cars doesn't have a Name property and both Students and Cars are in the list). If you edit a column for a property that doesn't exist on the object, you'll throw an exception. To get around this, you'll need a converter and style that applies it to the datagridcells. For fun, I also added a datatrigger that changes the background of the cell to Silver if it is disabled. One issue will be if you need to change the cell's style then you have to do it in the code (or change the style in the code to be based on your style).
XAML:
<DataGrid ItemsSource="{Binding ReportsTable}" AutoGenerateColumns="True" LoadingRow="DataGrid_LoadingRow" />
CS
private void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
var dg = sender as DataGrid;
var pis = e.Row.DataContext.GetType().GetProperties();
foreach (var pi in pis)
{
// Check if this property already has a column in the datagrid
string name = pi.Name;
var q = dg.Columns.Where(_ => _.SortMemberPath == name);
if (!q.Any())
{
// No column matches, so add one
DataGridTextColumn c = new DataGridTextColumn();
c.Header = name;
c.SortMemberPath = name;
System.Windows.Data.Binding b = new Binding(name);
c.Binding = b;
// All columns don't apply to all items in the list
// So, we need to disable the cells that aren't applicable
// We'll use a converter on the IsEnabled property of the cell
b = new Binding();
b.Converter = new ReadOnlyConverter();
b.ConverterParameter = name;
// Can't apply it directly, so we have to make a style that applies it
Style s = new Style(typeof(DataGridCell));
s.Setters.Add(new Setter(DataGridCell.IsEnabledProperty, b));
// Add a trigger to the style to color the background when disabled
var dt = new DataTrigger() { Binding = b, Value = false };
dt.Setters.Add(new Setter(DataGridCell.BackgroundProperty, Brushes.Silver));
s.Triggers.Add(dt);
c.CellStyle = s;
// Add the column to the datagrid
dg.Columns.Add(c);
}
}
}
CS for the converter:
public class ReadOnlyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
var prop = value.GetType().GetProperty(parameter as string);
if (prop != null)
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And, just to be complete, this is what I used to setup the data for the screenshot:
public List<IReports> ReportsTable { get; set; }
public MainWindow()
{
InitializeComponent();
ReportsTable = new List<IReports>() {
new Students() { Name = "Student 1" },
new Students() { Name = "Student 2" },
new Classes() { ClassName="CS 101", StudentName = "Student 3" },
new Cars() { CarType = "Truck", Mileage=12345, StudentName = "Student 4" }
};
this.DataContext = this;
}
Screenshot:
Instead of a converter option to display a given string value, why not add a getter to the base interface. Then, each class just returns its own, almost like every object can override its "ToString()" method Since you would create a list such as for display, or picking, the value would be read-only anyhow, Make it just a getter...
public interface IReports
{
string ShowValue {get;}
}
public class Students:IReports
{
public string Name { get; set; }
public string ShowValue { get { return Name; } }
}
public class Classes : IReports
{
public string ClassName { get; set; }
public string StudentName { get; set; }
public string ShowValue { get { return ClassName + " - " + StudentName ; } }
}
public class Cars : IReports
{
public int Mileage { get; set; }
public string CarType { get; set; }
public string StudentName { get; set; }
public string ShowValue { get { return CarType + "(" + Mileage + ") - " + StudentName; } }
}
Then in your view model manager...
public class YourMVVMClass
{
public YourMVVMClass()
{
SelectedRptRow = null;
ReportsTable = new List<IReports>()
{
new Students() { Name = "Student 1" },
new Students() { Name = "Student 2" },
new Classes() { ClassName="CS 101", StudentName = "Student 3" },
new Cars() { CarType = "Truck", Mileage=12345, StudentName = "Student 4" }
};
}
// This get/set for binding your data grid to
public List<IReports> ReportsTable { get; set; }
// This for the Selected Row the data grid binds to
public IReports SelectedRptRow { get; set; }
// This for a user double-clicking to select an entry from
private void Control_OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// Now, you can look directly at the SelectedRptRow
// as in the data-grid binding declaration.
if (SelectedRptRow is Classes)
MessageBox.Show("User selected a class item");
else if( SelectedRptRow is Cars)
MessageBox.Show("User selected a car item");
else if( SelectedRptRow is Students)
MessageBox.Show("User selected a student item");
else
MessageBox.Show("No entry selected");
}
}
Finally in your form/view
<DataGrid Grid.Row="0" Grid.Column="0"
ItemsSource="{Binding ReportsTable}"
SelectedItem="{Binding SelectedRptRow}"
MouseDoubleClick="Control_OnMouseDoubleClick"
AutoGenerateColumns="False"
Width="200" Height ="140"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<DataGrid.Columns>
<DataGridTextColumn
Header="Report Item"
Width="180"
IsReadOnly="True"
CanUserSort="False"
Binding="{Binding Path=ShowValue}" />
</DataGrid.Columns>
</DataGrid>
The other answers using the converters are just another path, but this avenue to me is easier as you can change each individual class and expand / adjust as needed. The exposed "ShowValue" getter is common to all instances of the "IReports", so the binding is direct without going through the converter. If you remove a class, or extend in the future, your underlying is all self-contained.
Now don't get me wrong, I do use converters and typically do with Boolean type fields to respectively show, hide, collapse controls as needed. This is nice as I have different boolean converters such as
BoolToVisibleHidden = if True, make visible vs Hidden
BoolToHiddenVisible = if True, make Hidden vs Visible
BoolToVisibleCollapse = if True, make visible vs Collapsed
BoolToCollapseVisible = if True, make Collapsed vs visible.
So, with one boolean property on my MVVM, I can both show AND hide different controls... maybe such as an admin vs standard user option.
I've also used converters dealing with dates for alternate formatting purposes.
You could abuse IValueConverter for that. Create one for each column.
In the ValueConverter you can test for the type and return the correct property. An example of what I mean:
public class NameValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Students)
{
return (value as Students).Name;
}
if (value is Classes)
{
return (value as Classes).ClassName;
}
if (value is Cars)
{
return (value as Cars).CarType;
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
To use it add it as resource to the DataGrid:
<DataGrid.Resources>
<local:NameValueConverter x:Key="NameValueConverter"></local:NameValueConverter>
</DataGrid.Resources>
And specify it in the binding like this:
{Binding Path=., Converter={StaticResource NameValueConverter}}
This solution would only work for read-only DataGrids though (editing throws a NotImplementedException).
I have got a DataGridComboBoxColumn that I need to translate with the WPFLocalizationExtension.
I have got a static method in my view model, that provides the available values:
public static List<ProfileSegmentType> AvailableSegmentTypes
{
get
{
var availableSegmentTypes = new List<ProfileSegmentType>
{
new ProfileSegmentType { Value = ProfileSegmentTypeEnum.Arc },
new ProfileSegmentType { Value = ProfileSegmentTypeEnum.Line },
};
return availableSegmentTypes;
}
}
The ProfileSegmentType looks like this:
public class ProfileSegmentType
{
public ProfileSegmentTypeEnum Value { get; set; }
public string Label
{
get { return Resources.Localization.ADummyValue; }
}
}
My data grid column definition looks like this:
<DataGridComboBoxColumn Header="..."
ItemsSource="{Binding Source={x:Static viewModels:ProfileGeometryViewModel.AvailableSegmentTypes}}"
SelectedValueBinding="{Binding SegmentType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Value"
DisplayMemberPath="Label" />
Now I get labels translated (right now the dummy value). But when I switch the language, the cells are not updated, of course. How can I achieve is, that the cell contents are updated, when I switch the language? (I also tried to use a value converter, but couldn't get it to work this way)
With the help of your comments, I could solve it. I had to change my ProfileSegmentType like so:
public class ProfileSegmentType : INotifyPropertyChanged
{
public ProfileSegmentType()
{
LocalizeDictionary.Instance.PropertyChanged += (e, a) =>
{
if (a.PropertyName == "Culture")
PropertyChanged(this, new PropertyChangedEventArgs("Label"));
};
}
public ProfileSegmentTypeEnum Value { get; set; }
public string Label
{
get { return Common.Resources.Localization.Add; }
}
public event PropertyChangedEventHandler PropertyChanged = (e, a) => {};
}
I have the following code in a WCF service:
[DataContract]
[KnownType(typeof(Bitmap))]
[KnownType(typeof(Image))]
public class CompositeType {
Image FImg = null;
public Image Picture {
get {
return FImg;
}
set {
FImg = value;
}
}
If I add [DataMember] to the public Image, then the Service Reference gets broken in another solution.
[DataMember]
public Image Picture{
get {
return FImg;
}
set {
FImg = value;
}
}
My question is how do I use [DataMember] and Image at the same time? I know I can use a byte array and am currently doing so and then formatting / converting it in the client that calls my service, but I'd rather bind to the Image instead of having to convert a byte array.
I've found that using the AutoGeneratingColumn event handle on the clientside (the Silverlight application calling my WCF service) works also. Not necessarily an answer to my question, but I think it's useful to know. I would've added as comment, but code is too long.
private void dgResults_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) {
if (e.PropertyType == typeof(byte[])) {
e.Column.Header = e.Column.Header + "_D";
// Create a new template column.
DataGridTemplateColumn templateColumn = new DataGridTemplateColumn();
templateColumn.Header = e.Column.Header + "_E";
templateColumn.CellTemplate = (DataTemplate)Resources["imgTemplate"];
templateColumn.CellEditingTemplate = (DataTemplate)Resources["imgTemplate"];
// ...
// Replace the auto-generated column with the templateColumn.
e.Column = templateColumn;
}
}
The Resources["imgTemplate"] are created in the .XAML file in Silverlight and this code is in its code-behind.
<UserControl.Resources>
<local:BinaryArrayToURIConverter x:Key="binaryArrayToURIConverter" />
<DataTemplate x:Key="imgTemplate">
<Image x:Name="img" Source="{Binding GraphicBytes,Converter={StaticResource binaryArrayToURIConverter}}"/>
</DataTemplate>
</UserControl.Resources>
The local: refers to part of the main XAML declaration:
xmlns:local="clr-namespace:<your namespace here>"
The code for BinaryArrayToURIConverter:
public class BinaryArrayToURIConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
MemoryStream ms = new MemoryStream((byte[])value);
BitmapImage image = new BitmapImage();
image.SetSource(ms);
return image;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}