.NET MAUI Communitytoolkit.Mvvm: How to bind an object instead of a string - xaml

i already the project from the official tutorial of .NET MAUI until step 5 using Communitytoolkit.Mvvm:
text
Now, instead of binding only a Text (which is a standard type that can be accessed from everywhere) i would like to bind a simple object (called ItemGroup) with two members (bool isChecked and string name).
How to do that?
For a global access i made this class in the MainView folder called ItemGroup. This class is not accessable and i don't know how to do that it is.
I changed the code in the MainPage.xaml like this:
<CollectionView ItemsSource="{Binding Items}"
Grid.Row="1">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{x:Type x:ViewModel.ItemGroup}">
<Grid >
<CheckBox IsChecked="{Binding ItemGroup.IsChecked}" Grid.Column="0"/>
<Label Text="{Binding ItemGroup.name}" Padding="10" Grid.Column="1"
BackgroundColor="LightGray"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
See also the project structure with the ItemGroup class in the ViewModel folder as well as the error message:
where the content page is declared as this:
Remark: The MainViewModel looks like this:
Should i declare some uses, or namespace?
where in the project should i place the Class of the objects i would bind?
Thanks in advance, Thomas
Also tryed to implement the class ItemGroup in the MainViewModel.cs but then i had any more problems with access to this class.

Try this
<Label Text="{Binding name}" …
Note that name must be a public property

You have a number of issues here.
The community mvvm toolkit includes code generators.
A variable:
[ObservableProperty]
string text;
Is examined by the code generator at compile time and in a partial class it will add a public property Text.
Binding is case sensitive and you need to bind to public properties so you need to use upper case on that first letter. Binding Text rather than Binding text.
You might have other instances where you did the same sort of thing.
I don't see where you use it but that add [Relaycommand] will generate AddCommand as a public property which you bind to.
Additionally you have misunderstood how an itemscontrol gets an item out a bound collection per row. As DevenCC points out, once you bind ItemsSource to Items then each row will get an instance of whatever is in that collection so you should bind IsChecked rather than ParentProperty.IsChecked.
As to where to put classes. In large apps it is a nuisance to flip between a folder contains views and another contains viewmodels. You might want to consider a folder contains each pair of view and viewmodel. Hence a foo folder containing fooView and fooViewModel

I believe your problem lies with you Binding declaration inside you ItemTemplate. Given that you bound your CollectionView's ItemSource to "items", the ItemTemplate's data context is now a single itemGroup from your list. Therefore, you should not be writing
<CheckBox IsChecked="{Binding ItemGroup.IsChecked}"[...]
But instead
<CheckBox IsChecked="{Binding isChecked}"[...]
Since you are already "within" a single itemgroup object. Same goes for your Label. Furthermore, I don't think you actually need to declare the Datatype for your item's DataTemplate.
p.s. Watch out for your item declaration, it seems like you have both items and Items declared in your ViewModel; and both seem public.

Related

Can a Binding value be created as a XAML Page resource?

Hi for rapid UI testing of Binding data at design-time in a UWP app, I was wondering if there was a way to create Binding values as resources in XAML. So instead of having to create a model, instantiate that model at design-time in Page.Resources, I just wanted to create essentially some constants I could use as Bindings.
Here are some examples of where I’d like the test bindings to work:
<TextBlock Text="{Binding title, ConverterParameter=lower, Converter={StaticResource StringFormatConverter}, Mode=TwoWay}" />
<TextBlock Text="{Binding title}" />
Given that scenario is there a way I could say define title as a named key with a string value that would work above?
Note that I am using Binding and not x:Bind since I want to use design-time data and I don’t want to have to rely on using the FallbackValue parameter.
Thanks for any ideas!
Rick
You can't bind to a static resource directly, but you can bind to a property of a static resource instead.
You can create a class implementing INotifyPropertyChanged which would have properties like Title. You would then create an instance of this class and store it as resource:
Application.Current.Resources[ "Data" ] = new MyDataClass();
Now you could use it in binding like this:
<TextBlock Text="{Binding Title, Source={StaticResource Data}}" />

Alternative to ElementName in x:Bind with DataTemplates

When using traditional {Binding} syntax you could specify element name to point to a specific control on the page, and be able to access its properties. For example if the page is named page you could do:
{Binding ElementName=Page, Path=Name}
With the {x:Bind} syntax it says
With x:Bind, you do not need to use ElementName=xxx as part of the
binding expression. With x:Bind, you can use the name of the element
as the first part of the path for the binding because named elements
become fields within the page or user control that represents the root
binding source.
So for the example above in {x:Bind} would be
{x:Bind page.Name}
Which works fine, until it is inside a data template (for example a ListView's ItemTemplate). In which case it no longer works as it is looking for Page on the data type specified which leads to the following error (assuming my data type is customer):
XamlCompiler error WMC1110: Invalid binding path 'Page.Name' :
Property 'Page' can't be found on type 'Customer'
What is the solution to use {x:Bind} syntax with datatemplates and access controls outside the data template?
Example code is available here (note specific commit)
As far as I know at this point in time there is no way to directly bind to a property of a control using the x:bind method as it does not support the element name inside of its binding definition.
That does not mean you cant bind to a control inside a dataTemplate you can still do something like this to access controls but you just aren't able to use the compiled binding x:Bind syntax.
<DataTemplate x:DataType="local:Customer">
<StackPanel Orientation="Vertical">
<Button Content="{Binding Name, ElementName=page}" />
<TextBlock Text="{x:Bind Title}" />
</StackPanel>
</DataTemplate>
The reason for the error you are getting is due to the way data templates parent their datasource. The x:Bind binding cannot reference a control object and your Customer type does Page.Name property or path. As shown above the only real way of accessing user control properties outside of your control only using XAML is to resort back to the standard binding mechanism.
I hope this answers your question.

What's the DataContext of the Header elements inside a Hub control?

I have started to develop for WP 8.1 using Windows Runtime recently, and I have faced a... "problem" that I don't seem to understand.
The application I am currently developing uses a Hub control, and I would like to tweak its header's appearance slightly. To do so, I changed the HeaderTemplateattribute of the Hub control. However, as I tried to localize the textual content of the header (note that this is WinRT, the localization process is slightly different from the process in WP8 and can be found here), I "accidentally" fixed the problem by making the template like this:
<Hub.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
Foreground="Red"/>
</DataTemplate>
</Hub.HeaderTemplate>
I don't understand why it worked, though. When you do data binding like this (just using the expression {Binding}), doesn't the element get the same DataContext as its father? What's happening under the hood? Who's the parent element of the TextBlock, after all?
Thanks in advance.
EDIT
As igrali asked, here is a more complete view of the XAML:
<Page ...
DataContext="{Binding Data, RelativeSource={RelativeSource Self}}">
<Grid Background="#FFF6DB">
<Hub Name="MainPageHub"
x:Uid="MainPageHub"
Margin="0,27,0,0">
<Hub.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
Foreground="White"/>
</DataTemplate>
</Hub.HeaderTemplate>
...
Then, in the /Strings/en-US/Resources.resx, I have a "MainPageHub.Header" property set to "foobar" (just an example), and what I get as a header is actually "foobar" (which is what I wanted, but even so it seems confusing!).
As Tim Heuer explains it here
Notice the x:Uid value here. This now maps back to the key in your
RESW file. Anything with that starting key will have properties
merged into it. So we can have a key in our RESW for “Text” using the
key name MyTextBlock.Text with a value of “Hello World” and the
runtime will do the replacement for you. This applies to properties
other than text (i.e. width) as well as attached properties.
All I can add is - notice the x:Uid. It's MainPageHub. In the localized resource file, you have a MainPageHub.Header. This means that the value of the resource string will be set to the Header of the control which has the x:Uid set to MainPageHub.
So, now that it's clear how the Header is set - there's still the binding part. Well, considering you have a different template for the Header, it needs to do the {Binding } part to actually get the value of the header which is set through the resources.

Windows Phone xaml element name

In Windows Phone xaml page, some element has x:Name
<TextBlock x:Name="PageTitle" Text="simple" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
While others have Name
<TextBlock Name="tbSource" Height="30" HorizontalAlignment="Left" Margin="129,178,0,0" Text="Source" VerticalAlignment="Top" Width="304" />
What is the difference ?
The x: part is a namespace alias. It tells the compiler where the property exists. In this case it's pointing to the default namespace for XAML.
The Name property is part of System.Object which is in that namespace.
When you include x:Name to identify a property called Name in XAML what you're explicitly saying is that you're referring to the Name property of the underlying System.Object. As all objects inherit from this it's accessible to all classes.
Why might any of this matter?
If you overrode the Name property in a class then specifying the namespace will allow you to be explicit about which version of Name you're referring to.
Side note: you will save yourself a lot of pain by never overriding Name.
Does it matter which you use?
No. As long as Name is never overridden and you need to refer to the specific version of it.
It's probably in your interest to be consistent in whether you use it or not. Most people do (as does VS when it creates controls for you) so it's probably easiest to keep them there.
Not sure, but I think x:Name elements are accessible in code by their name. e.g. PageTitle.Text = "some text". And "Name" is just a regular property of an element. Please also see the following article about x:Name directive.

Can Ag UserControl expose properties exposed for XAML to bind to?

I have a new UserControl backed by UserControl01.cs that is exposing two properties. One is a plain old CLR property, the other the newer DP property format.
eg:
string PropertyOne {get;set;}
and the second property is a public DP (construct is too long to list here).
From the XAML side, I wish to expose these value. How can I do that?
On both properties (old CLR as well as second DP wrapper format) I've tried syntax like:
<TextBlock Text={Binding Prop1}/>
<TextBlock Text={Binding Source=., Path=Prop1}/>
I've tried putting a ref to the class type in the Resource dictionary and referencing it with
<TextBlock Text={Binding Source={StaticReference myType}, Path=Prop1}/>
but that obviously ends up being a stack overflow as the dictionary instantiates a second copy of the Control.
I read a post somewhere that in WPF one can x:Name the control itself, and then
<TextBlock Text="{Binding {ElementName=theName, Path=Prop1}"/>
but we don't have ElementName binding in Ag, so that's not the way.
How can I bind to the UserControl's property? Any property!
If that's not possible, and you can only bind to DTO's or other BO's, what's the guiding logic/explanation as to how we should design UserControls that want to expose their values?
Silverlight 3 has control to control binding.
http://silverlight.net/learn/learnvideo.aspx?video=187309