NET MAUI binding elemens of an 2D multidimensional - c#

what would be the way to bind an element of an 2D multidimensional array in NET MAUI?
I tried to do the following:
<Label Text="{Binding Calendar.MatrixMonth[0,0]}"
Style="{StaticResource subTitleLightTextStyle}"
Grid.Row="2"
HorizontalOptions="Start" />
But that doesn't work, it just shows the error:
No constructor for type 'BindingExtension' has 2 parameters

It seams that you are trying to use the Binding markup extension (which only accepts one parameter as the path of property to bind) You need something like x:Bind in UWP's which unfortunately is not yet supported in MAUI out of the box. But there is a library that allows you to do so here.
So you can try this code (not tested):
<Label Text="{x:Bind Calendar.MatrixMonth[0,0]}"
Style="{StaticResource subTitleLightTextStyle}"
Grid.Row="2"
HorizontalOptions="Start" />

You could use ValueConverter to bind a 2D array as an alternative. Try the following code:
In xaml,
//define a value converter
<ContentPage.Resources>
<local:ArrayToStringConverter x:Key="arrayToStringConverter"/>
</ContentPage.Resources>
...
// for label, consume this converter and pass the parameter "1,1" as index
<Label FontSize="32" HorizontalOptions="Center" Text="{Binding Calendar.MatrixMonth,
Converter={StaticResource arrayToStringConverter},
ConverterParameter='1,1'}">
And you could generate a new file to do value converter:
public class ArrayToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var a = value as string[,];
string p = parameter.ToString();
var index1 = int.Parse(p.Split(",")[0]);
var index2 = int.Parse(p.Split(",")[1]);
return a[index1, index2];
}
....
}
For more info, you could refer to Binding value converters
Hope it works for you.

Related

x:Bind DataTemplate in DataGridTemplateColumn.CellTemplate is not displaying content

Following the response on this windows-toolkit issue, I'm using x:Bind to bind elements of an ObservableCollection of AlertEntry's to DataGridColumn cells. My XAML is as follows:
<controls:DataGrid ItemsSource="{x:Bind ViewModel.Alerts, Mode=OneWay}" AutoGenerateColumns="True" IsReadOnly="True">
<controls:DataGrid.Columns>
<controls:DataGridTemplateColumn Header="Time" >
<controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate x:DataType="ace:AlertEntry">
<TextBlock Text="{x:Bind Timestamp, Converter={StaticResource StringFormatConverter}, ConverterParameter='{}{H:mm:ss}'}"/>
</DataTemplate>
</controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>
</controls:DataGrid.Columns>
</controls:DataGrid>
And my AlertEntry class:
public class AlertEntry
{
public DateTime Timestamp;
public ACEEnums.AlertLevel Level;
public ACEEnums.AlertType Type;
public string Info;
public AlertEntry(ACEEnums.AlertLevel level, ACEEnums.AlertType type, string info = "")
{
Timestamp = DateTime.Now;
Level = level;
Type = type;
Info = info;
}
}
When elements are added to ViewModel.Alerts I can see highlightable rows are added to the DataGrid, but they display no content. When I remove the binding and add a fixed text value, it displays that value correctly every time a row is added.
The AlertEntry items in ViewModel.Alerts correctly contain data.
I've confirmed the StringFormatConverter works in other bindings. In fact the StringFormatConverter is not ever being called.
I'm using MVVM-Light and UWP.
Thank you!
For the testing, the problem may occur in your StringFormatConverter, TextBlock Text property only allow string value, So we need return string type value in Convert method.
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return null;
if (parameter == null)
return value;
var dt = (DateTime)value;
return dt.ToString((string)parameter);
}
And x:Bind support bind function , you could try use Text="{x:Bind Timestamp.ToString()}" to verify above.
My DataGrid is an element of a StackPanel and I was declaring my converters as static resources of the StackPanel like so:
<StackPanel.Resources>
<helper:StringFormatConverter x:Key="StringFormatConverter" />
<helper:AlertToString x:Key="AlertToString" />
</StackPanel.Resources>
I moved the converters to be resources of the whole page instead, and at that point they started working.
<Page.Resources>
<helper:StringFormatConverter x:Key="StringFormatConverter" />
<helper:AlertToString x:Key="AlertToString" />
</Page.Resources>

Loading Images from resource to ListView

I want to bind Image in a listView. Images are saved in Resources Dir and picking will be based on Status parameter which is get from json Array.
I' ve found out that when it is processing, ImageConverter class is not even touched when UI is created. As a result I get listview with empty space for image and filled (as expected) labels.
Question is: How to make it work ?
And
Is there simpler way to perform such operation
XAML FILE:
<ListView x:Name="contentList"
RowHeight="125"
VerticalOptions="FillAndExpand"
SeparatorVisibility="Default"
SeparatorColor="Black"
BackgroundColor="White"
ItemSelected="onOrderSelected"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout VerticalOptions="FillAndExpand"
Orientation="Horizontal"
BackgroundColor="White"
>
<Image Source="{Binding Status,Converter={StaticResource ImageConverter }}"
WidthRequest="100"
HeightRequest="100
/>
<StackLayout Orientation="Vertical">
<Label Text="{Binding Title}"
FontSize="Default"
VerticalOptions="Center"
Margin="20,0,0,0"
TextColor="Black"/>
.
.
.
.
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
CONVERTER:
class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string filename = value as string;
return ImageSource.FromStream(() => new MemoryStream(DependencyService.Get<IWRDependencyService>().GetImageBytes(filename)));
}
}
Android Interface Implementation
class ImageLoader : IWRDependencyService
{
public byte[] GetImageBytes(string fileName)
{
fileName = fileName.Replace(".jpg", "").Replace(".png", "");
var resId = Forms.Context.Resources.GetIdentifier(
fileName.ToLower(), "drawable", Forms.Context.PackageName);
var icon = BitmapFactory.DecodeResource(Forms.Context.Resources, resId);
var ms = new MemoryStream();
icon.Compress(Bitmap.CompressFormat.Png, 0, ms);
byte[] bitmapData = ms.ToArray();
return bitmapData;
}
}
As a result I get listview with empty space for image and filled (as expected) labels. Question is: How to make it work ? And Is there simpler way to perform such operation
For Converter to work in Xamarin.Forms, you need to declare the converter in Xaml like below:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ImageListViewDemo"
x:Class="ImageListViewDemo.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:MyImageConverter x:Key="ImageConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>
Then the converter will be triggered correctly.Here is the basic demo that I made from your codes:ImageListViewDemo.
If your converter is passed the end resulting status value from your JSON you should just need to return the ImageSource according to whatever the status value is since your images are already in the resource directory.
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var status = value as WhateverType;
if (status == someValue1)
{
return ImageSource.FromFile("status1Image.png");
}
else if(status == someValue2)
{
return ImageSource.FromFile("status2Image.png");
}
......
}
Also if it's not going into your converter at all you might want to make sure your Converter is defined either in your App.xaml or the Resources of the Xaml file it's in.
<ListView.Resources>
<ResourceDictionary>
<ImageConverter x:Key="imageConverter" />
</ResourceDictionary>
</ListView.Resources>

How to check or uncheck checkboxes inside of a List Box WPF

I have a combobox which data source comes from a table in my DataBase. So, each item in my combo is an Object from the table. This Object have an attribute which corresponds to a string full of "1"s or "0"s. On the other hand I have a list of checkboxes inside of a ListBox with this template:
<ListBox Height="150" MinHeight="100" HorizontalAlignment="Center" Name="lstEstudios" VerticalAlignment="Top" Width="200"
ItemsSource="{Binding}" SelectionMode="Multiple" Margin="0,20,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Name="chkEstudios" Width="Auto" Content="{Binding Path=Nom_estudio}"
Checked="chkEstudios_Checked" Unchecked="chkEstudios_Unchecked"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I donĀ“t know if it's possible but, that I want to do is, for each "1" or "0" in the attribute set the checkbox checked or unchecked depending if there is a "1" check the checkbox or if is "0" uncheck the checkbox, and so on... with all the checkboxes in the ListBox, how to do that ?
I tried the same thing with my own sample having a CustomTask class.
<ListBox ItemsSource="{Binding CustomTasks}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding TaskStatus, Converter={x:Static testApp:StatusToBooleanConverter.Instance}}" Content="{Binding TaskStatus}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
where the TaskStatus is a boolean of two values, i.e Completed and Pending.
and here is the code for the converter
public class StatusToBooleanConverter : IValueConverter
{
public static StatusToBooleanConverter Instance = new StatusToBooleanConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Status)
{
switch ((Status)value)
{
case Status.Completed:
return true;
case Status.Pending:
return false;
}
return null;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Just try this out. Hope that helps.
This is a good place to use a converter. If you have a property with 1's and 0's in it and you want to translate those to true/false for the checked attribute then try making converters to do the work. The basic idea behind a converter is to take an input value (from a bound property) and, as the name implies, convert it to a different value. You can make them as simple or complicated as you would like, and turning "1" into true and "0" into false should be pretty quick.
You will bind the IsChecked attribute of the checkbox to your source of 1's and 0's and in the binding also use the converter.
If you haven't made a value converter before here is a nice tutorial on making one: http://wpftutorial.net/ValueConverters.html

Set the content of Label to Image's Tooltip

I am using IValueconverter interface to change the tooltip text of an image.
The tool tip should change based on label.
<Label Content="9898980001" Height="28" HorizontalAlignment="Left" Margin="1733,231,0,0" Name="lbl02scanning" VerticalAlignment="Top" Foreground="Blue" >
<Image Height="49" HorizontalAlignment="Right" Margin="0,131,113,0"
Name="img02scanning"
Source="/TEST;component/Images/LoadingStation.png" Stretch="Fill"
VerticalAlignment="Top" Width="30" Cursor="Hand">
<Image.ToolTip>
<StackPanel Background="AliceBlue">
<TextBlock Padding="5" Foreground="White" MinHeight="20"
Background="Blue" FontWeight="Bold"
Text="Scanning Station" />
<StackPanel Orientation="Horizontal">
<Image
Source="pack://application:,,,/TEST;component/Images/coilonsaddle_large.png"
Height="100" Width="100" />
<TextBlock Padding="10" TextWrapping="WrapWithOverflow"
MaxWidth="200" Background="AliceBlue"
Foreground="Black" FontWeight="Bold"
Text="{Binding ElementName=lbl02scanning, Path=Name,
ConverterParameter=255,
Converter={StaticResource FormatterFOrCoilToolTip}}"/>
</StackPanel>
<TextBlock Padding="5" Foreground="White" MinHeight="20"
Background="Blue" FontWeight="Bold"
Text="Report to admin in case of coil location mismatch"/>
</StackPanel>
</Image.ToolTip>
</Image>
The converter class:
public class FormatterForCoilToolTip : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if(parameter.ToString() == "02")
{
return value.ToString() + " Startin";
}
else
{
return value.ToString() + " Finishing";
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The tooltip's Textblock content is not changing. But if i change to:
Text="{Binding ConverterParameter=255, Converter={StaticResource FormatterFOrCoilToolTip}}
then it is working. But i want to pass the lbl02scanning text value. Why it is not working??
First of all you should bind to Content property and not Name property in case you want Text of Label.
Most importantly Tooltip does not lies in same Visual Tree as that of label, hence binding with elementName won't work. However, you can use x:Reference to get the element even if it doesn't exist in same Visual Tree.
Text="{Binding Source={x:Reference lbl02scanning}, Path=Content,
ConverterParameter=255,
Converter={StaticResource FormatterFOrCoilToolTip}}"/>
Note - x:Reference is introduced in WPF 4.0. If you are using WPF 3.5 you can't use this.
Update for error - service provider is missing the name resolver service
Just found out bug is reported at Microsoft site that x:Reference fails in case Target is Label. However, i couldn't reproduce this issue at my end since i have WPF 4.5 installed at my end and i guess they have fixed the issue in future version.
In case you target WPF 4.0, i would advise you to use TextBlock in place of Label:
<TextBlock Text="9898980001" Height="28" HorizontalAlignment="Left"
Margin="1733,231,0,0" Name="lbl02scanning" VerticalAlignment="Top"
Foreground="Blue" />
and then bind with Text property instead of Content.
Text="{Binding Source={x:Reference lbl02scanning}, Path=Text,
ConverterParameter=255,
Converter={StaticResource FormatterFOrCoilToolTip}}"/>
Either, you can refer to workaround provide under workarounds section here.
You can override the ProvideValue method of the Reference class and skip the reference search login in design time:
[ContentProperty("Name")]
public class Reference : System.Windows.Markup.Reference
{
public Reference()
: base()
{ }
public Reference(string name)
: base(name)
{ }
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget valueTargetProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (valueTargetProvider != null)
{
DependencyObject targetObject = valueTargetProvider.TargetObject as DependencyObject;
if (targetObject != null && DesignerProperties.GetIsInDesignMode(targetObject))
{
return null;
}
}
return base.ProvideValue(serviceProvider);
}
Update with another workaround
This will work for all versions WPF 3.5, WPf 4.0 and WPF 4.5.
First of all bind Image Tag with content of label.
Second host your stackPanel inside ToolTip control so that you can
take benefit of PlacementTarget property.
Third bind with PlacementTarget.Tag of Tooltip.
Relevant code will look like this:
<Image Tag="{Binding ElementName=lbl02scanning,Path=Content}">
<Image.ToolTip>
<ToolTip>
<TextBlock Text="{Binding RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=ToolTip},
Path=PlacementTarget.Tag,
ConverterParameter=255,
Converter={StaticResource FormatterFOrCoilToolTip}}"/>
</ToolTip>
</Image.ToolTip>
</Image>
Also you need to update converter code to put null check over there since PlacementTarget will be null until you open tooltip.
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value != null)
{
if (parameter.ToString() == "02")
{
return value.ToString() + " Starting";
}
else
{
return value.ToString() + " Finishing";
}
}
return String.Empty;
}
Try This
Text="{Binding Path=Content,ElementName=lbl02scanning, ConverterParameter=255, Converter={StaticResource FormatterFOrCoilToolTip}}

How to display dynamic culture formatted number in a WPF UserControl

I would like to dynamically set the culture format of the Number textblock with culture and number values passed through to MyUserControl. The MyCulture and Number values are passed to MyCustomControl and will be of the form "en-GB", "en-US" etc.
I did something similar in asp.NET MVC with an extension method but need help for how to piece this together in WPF.
Example MVC Extension Method
public static MvcHtmlString CulturedAmount(this decimal value,
string format, string locale)
{
if (string.IsNullOrEmpty(locale))
locale = HttpContext.Current.Request.UserLanguages[0];
return MvcHtmlString.Create(value.ToString(format,
CultureInfo.CreateSpecificCulture(locale)));
}
Window
//MyMoney is a decimal, MyCulture is a string (e.g. "en-US")
<MyCustomControl Number="{Binding MyMoney}" Culture="{Binding MyCulture}"
Text="Some Text" />
MyCustomControl
<StackPanel>
<TextBlock Text="{Binding Number, ElementName=BoxPanelElement,
StringFormat={}{0:C}}" /> //display this with specific culture
<TextBlock Text="{Binding Text, ElementName=BoxPanelElement}" />
</StackPanel>
If I understand your question correctly you want to bind the culture for a specific TextBlock.
You can't bind the properties of a Binding so binding ConverterCulture won't work.
There is a Language property on FrameworkElement which works fine to set like this
<TextBlock Language="en-US"
Text="{Binding Number,
ElementName=BoxPanelElement,
StringFormat={}{0:C}}"/>
However, when trying to bind this property I get a weird exception
I'm probably going to ask a question on this exception myself
Binding for property 'Language' cannot use the target element's
Language for conversion; if a culture is required, ConverterCulture
must be explicitly specified on the Binding.
According to this answer by Thomas Levesque this should be possible though so maybe I did something wrong.. WPF xml:lang/Language binding
All I got working was using an attached behavior which in turn updated Language when MyCulture updated.
<TextBlock local:LanguageBehavior.Language="{Binding MyCulture}"
Text="{Binding MyNumber,
ElementName=BoxPanelElement,
StringFormat={}{0:C}}"/>
LanguageBehavior
public class LanguageBehavior
{
public static DependencyProperty LanguageProperty =
DependencyProperty.RegisterAttached("Language",
typeof(string),
typeof(LanguageBehavior),
new UIPropertyMetadata(LanguageBehavior.OnLanguageChanged));
public static void SetLanguage(FrameworkElement target, string value)
{
target.SetValue(LanguageBehavior.LanguageProperty, value);
}
public static string GetLanguage(FrameworkElement target)
{
return (string)target.GetValue(LanguageBehavior.LanguageProperty);
}
private static void OnLanguageChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = target as FrameworkElement;
element.Language = XmlLanguage.GetLanguage(e.NewValue.ToString());
}
}
It seems like a converter is the answer. The interface includes a values for culture.
Convert(object value, Type targetType, object parameter, CultureInfo culture)
But I could not find syntax for passing culture.
Sorry this is not a full and tested answer but I ran out of time.
URL on binding culture.
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.converterculture.aspx
The syntax for passing a a parameter is:
Converter={StaticResource colorConverter}, ConverterParameter=GREEN}"
You may need to pass culture as a string using ConverterParameter.
I agree with Meleak that cannot bind the parameter to a converter. Gave him a +1.
But I think you can fool it with a MultiBinding converter.
<TextBlock Name="textBox2" DataContext="{StaticResource NameListData}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource myCutlureConverter}"
ConverterParameter="FormatLastFirst">
<Binding Path="InputValue"/>
<Binding Path="CultureTxt"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>

Categories