can someone please tell me the exact difference between FallbackValue and TargetNullValue? I know they are quite similar, but I'd like to understand more about those edge usecases where you want to use either one of the two or maybe usecases where using both makes sense.
Cheers!
Basically,
FallbackValue is set when the binding itself fails.
TargetNullValue is set when the source of the binding is null.
These are the results I got with WinUI 3.
Plain binding
public string TestText { get; set; } = "Binding succeeded!";
<TextBlock Text="{x:Bind TestText}" />
OR
<TextBlock Text="{Binding TestText}" />
The TextBlock shows "Binding succeeded".
Case#1
x:Bind, FallbackValue/TargetNullValue, wrong name
public string? TestText { get; set; } = "Binding succeeded!";
<TextBlock Text="{x:Bind Test, FallbackValue='Binding failed!'}" />
OR
<TextBlock Text="{x:Bind Test, TargetNullValue='Source is null!'}" />
You get a compile error because x:Bind checks the source at compile time.
Case#2
x:Bind, FallbackValue, null source
public string? TestText { get; set; } = null;
<TextBlock Text="{x:Bind TestText, FallbackValue='Binding failed!'}" />
The TextBlock shows nothing (empty).
Case#3
Binding, FallbackValue, wrong name
public string TestText { get; set; } = "Binding successed!";
<TextBlock Text="{Binding Test, FallbackValue='Binding failed!'}" />
The TextBlock shows the FallbackValue "Binding failed!".
Case#4
x:Bind, TargetNullValue, null source
public string? TestText { get; set; } = null;
<TextBlock Text="{x:Bind TestText, TargetNullValue='Source is null!'}" />
The TextBlock shows the TargetNullValue "Source is null!".
Case#5
Binding, TargetNullValue, null source
public string? TestText { get; set; } = null;
<TextBlock Text="{Binding TestText, TargetNullValue='Source is null!'}" />
The TextBlock shows nothing (empty).
Case#6
Binding, FallbackValue, TargetNullValue, null source
public string? TestText { get; set; } = null;
<TextBlock Text="{Binding TestText, FallbackValue='Binding failed!', TargetNullValue='Source is null!'}" />
The TextBlock shows the FallbackValue "Binding failed!".
Case#7
x:Bind, FallbackValue, TargetNullValue, null source
public string? TestText { get; set; } = null;
<TextBlock Text="{x:Bind TestText, FallbackValue='Binding failed!', TargetNullValue='Source is null!'}" />
The TextBlock shows the TargetNullValue "Source is null!".
Case#8
x:Bind/Binding, TargetNullValue, null ViewModel
public ViewModel? ViewModel { get; set; } = null;
<TextBlock Text="{x:Bind ViewModel.TestText, TargetNullValue='Source is null!'}" />
The TextBlock shows nothing (empty).
Case#9
x:Bind/Binding, FallbackValue, null ViewModel
public ViewModel? ViewModel { get; set; } = null;
<TextBlock Text="{x:Bind ViewModel.TestText, FallbackValue='Binding failed!'}" />
The TextBlock shows the FallbackValue "Binding failed!".
Related
I'm using WinUI in combination with the microsoft MVVM toolkit.
However im experiencing some issues with Binding and can't figure out where the problem lies.
The ViewModel and models used within the ViewModel are of type observableObject. The Command is fired, and the data is fetched. However the binding is not showing a result in the UI, unless i change the xaml and hot reload the change.
My page:
<Page
x:Class="ThrustmasterGuide.Pages.WheelBasePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ThrustmasterGuide.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:ThrustmasterGuide.DataAccess.Context.Model"
xmlns:wheelbase="using:ThrustmasterGuide.ViewModel.Wheelbase"
xmlns:xaml="using:ABI.Microsoft.UI.Xaml"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:converters="using:ThrustmasterGuide.Converters"
xmlns:wheelBase="using:ThrustmasterGuide.Model.WheelBase"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance wheelbase:WheelBaseViewModel, IsDesignTimeCreatable=True}">
<Page.Resources>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<converters:InvertBoolToVisibilityConverter x:Key="InvertBoolToVisibilityConverter" />
</Page.Resources>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Loaded">
<core:EventTriggerBehavior.Actions>
<core:InvokeCommandAction Command="{x:Bind ViewModel.LoadWheelBaseCommand}" />
</core:EventTriggerBehavior.Actions>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<StackPanel Padding="16 16 16 16" Orientation="Vertical">
<StackPanel>
<TextBlock FontSize="18" Text="{x:Bind ViewModel.WheelBase.Name}" />
<TextBlock FontSize="18" Text="Symptomen:" />
<TextBlock Text="Kies hieronder een symptoom uit om te starten." />
<ProgressRing IsActive="true"
Visibility="{x:Bind ViewModel.LoadWheelBaseCommand.IsRunning, Converter={StaticResource BoolToVisibilityConverter}}" />
<TreeView ItemsSource="{x:Bind ViewModel.WheelBase.Symptoms, Mode=OneWay}">
<TreeView.ItemTemplate>
<DataTemplate x:DataType="wheelBase:SymptomModel">
<TreeViewItem ItemsSource="{x:Bind Children}" Content="{x:Bind Description}" />
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
<Button VerticalAlignment="Bottom" Command="{x:Bind ViewModel.LoadWheelBaseCommand}"
Content="Refresh">
</Button>
</StackPanel>
My ViewModel:
public class WheelBaseViewModel : ObservableRecipient
{
public WheelBaseModel WheelBase { get; set; }
public string WheelBaseName { get; set; }
private readonly WheelBaseService _wheelBaseService;
public IAsyncRelayCommand LoadWheelBaseCommand { get; }
public WheelBaseViewModel(WheelBaseService wheelBaseService)
{
_wheelBaseService = wheelBaseService;
LoadWheelBaseCommand = new AsyncRelayCommand(FetchWheelBase);
}
public async Task FetchWheelBase()
{
WheelBase = await _wheelBaseService.GetWheelBase(WheelBaseName);
}
}
My model:
namespace ThrustmasterGuide.Model.WheelBase
{
public class WheelBaseModel : ObservableObject
{
public string Name { get; set; }
public ObservableCollection<SymptomModel> Symptoms { get; set; }
}
}
My Code behind:
public sealed partial class WheelBasePage : Page
{
public WheelBasePage()
{
this.InitializeComponent();
this.DataContext = App.Current.Services.GetService<WheelBaseViewModel>();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
this.ViewModel.WheelBaseName = e.Parameter as string;
}
public WheelBaseViewModel ViewModel => (WheelBaseViewModel)DataContext;
}
What is it that i missed to make the UI bind to the WheelBaseModel values?
Update I added mode=OneWay, but still not updating.
Should it be noted that im showing pages within a content frame after navigation?
{x:Bind} has a default mode of OneTime, unlike {Binding}, which has a default mode of OneWay.
I believe you need to use SetProperty so it's known when to raise such events?
https://learn.microsoft.com/en-us/windows/communitytoolkit/mvvm/observableobject
namespace ThrustmasterGuide.Model.WheelBase
{
public class WheelBaseModel : ObservableObject
{
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
public ObservableCollection<SymptomModel> Symptoms { get; set; }
}
and also bind mode = OneWay
<TextBlock FontSize="18" Text="{x:Bind ViewModel.WheelBase.Name, Mode=OneWay}" />
No indication that your properties notify of their changes.
https://learn.microsoft.com/dotnet/api/system.componentmodel.inotifypropertychanged
I'm doing a cross platform app using Xamarin. I want to bind a ListView with a query.
My query looks like this :
public async Task<List<Meeting>> GetAllMeetings()
{
return await _database.QueryAsync<Meeting>("Select Client.Prenom,Meeting.Id_Client, Meeting.DateRDV, Meeting.HourRDV,Meeting.TypePose,Meeting.IsStudent from 'Meeting' JOIN 'Client' on Meeting.Id_Client = Client.Id");
}
In my ListView I'm able to bind every parameter, except Client.Prenom. I've tried only writing Prenom but nothing shows.
Here's how I bind :
listViewAllMeetings.ItemsSource = await App.Database.GetAllMeetings();
List<Meeting> list = listViewAllMeetings.ItemsSource as List<Meeting>;
And in the xaml file I do like this to bind Prenom:
<Label Text="{Binding Prenom}"
HorizontalOptions="Start"
TextColor="#000000" />
But nothing shows. Any idea ? If I bind the Id_Client it shows the correct Id...
For the record , here's my Meeting table :
public class Meeting {
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Id_Client { get; set; }
public String DateRDV { get; set; }
public String HourRDV { get; set; }
public String TypePose { get; set; }
public bool IsStudent { get; set; }
public bool SheCame { get; set; }
}
my xaml file :
<ListView x:Name="listViewAllMeetings" SeparatorVisibility="Default" SeparatorColor="Silver" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout
Orientation="Vertical">
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding DateRDV}"
HorizontalOptions="Start"
TextColor="#FD6C9E" />
<Label Text="à"
HorizontalOptions="Start"
TextColor="#FD6C9E" />
<Label Text="{Binding HourRDV}"
HorizontalOptions="Start"
TextColor="#FD6C9E" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Id_Client}"
HorizontalOptions="Start"
TextColor="#FD6C9E" />
<Label Text="Est étudiante ?"
HorizontalOptions="End"
TextColor="#FD6C9E" />
<Label Text="{Binding IsStudent}"
HorizontalOptions="End"
TextColor="#FD6C9E" />
</StackLayout>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding TypePose}"
HorizontalOptions="Start"
TextColor="#FD6C9E" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
this line will take the results of your Query and map it into a Meeting object
return await _database.QueryAsync<Meeting>("Select Client.Prenom,Meeting.Id_Client, Meeting.DateRDV, Meeting.HourRDV,Meeting.TypePose,Meeting.IsStudent from 'Meeting' JOIN 'Client' on Meeting.Id_Client = Client.Id");
your Meeting class does not have any property that Client.Prenom can be mapped onto
Try putting ItemSource on your List,
Just edit this line in your xaml page, keep rest things as same
<ListView x:Name="listViewAllMeetings" ItemsSource="{Binding Path=Meetings}" SeparatorVisibility="Default" SeparatorColor="Silver" HasUnevenRows="True">
Just add this to your parent list ItemsSource="{Binding Path=Meeting}". I guess it will work for you.
Bind the Meeting with the query in your ViewModel.
EDIT ---------
//initialize these variables in your xaml.cs file
ObservableCollection<Meeting> _meetings { get; set; }
ObservableCollection<Meeting> Meetings { get { return _meetings} set { _meetings = value; NotifyPropertyChanged() } }
//Add this in your constructor of xaml.cs file
Meetings = App.Database.GetAllMeetings(); //Add
Correct your query as suggested by Jason
Add the Prenom refrence in Meeting.
public class Meeting {
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Prenom{ get; set; } //ADD here
.....
}
Let me know if it works or not.
How do you bind an item in a data template to the item itself, instead of a property of that item?
I have a user control that takes an item as a model. Given these models:
public class Car
{
public string Name { get; set; }
public Color color { get; set; }
public string Model { get; set; }
}
public MyUserControl : UserControl
{
public Car Model { get; set; }
}
public MyPage : Page
{
public ObservableCollection<Car> CareList { get; set; }
}
I want to do something like this in XAML:
<ListView ItemsSource="{x:Bind CarList}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:Car">
<StackPanel>
<!-- Binding to properties of Car is simple... -->
<TextBlock Text="{x:Bind Name}">
<!-- But what if I want to bind to the car itself ??? -->
<userControls:MyUserControl Model="{x:Bind Car}">
</userControls:MyUserControl>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The comment from #user2819245 is correct, if you do not specify the Path of the binding then the DataContext object will be bound directly instead.
In UWP, if your list source can change at runtime or is delay loaded, then you may also need to specify the Mode=OneWay, this is due to {x:Bind} defaulting to a OneTime binding mode.
This example includes how to set the Mode property in both use cases
<ListView ItemsSource="{x:Bind CarList, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:Car">
<StackPanel>
<!-- Binding to properties of Car is simple... -->
<TextBlock Text="{x:Bind Name, Mode=OneWay}">
<!-- Binding to the car itself (as the DataContext of this template) -->
<userControls:MyUserControl Model="{x:Bind Mode=OneWay}">
</userControls:MyUserControl>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have a condition where I want to add multiple PivotItems(which is dynamic).
ie; I have a List<CustomModel> whose size is dynamic, And for every item in the List I want to create a PivotItem with header as CustomModel.Title.
Is it possible to achieve this with xaml alone by creating a DataTemplate and binding it to a Pivot?
It is surely possible. See below sample solution
<Pivot x:Name="TestPivot">
<Pivot.HeaderTemplate>
<DataTemplate x:DataType="local:TestClass">
<TextBlock Text="{Binding HeaderTitle, Mode=OneWay}"/>
</DataTemplate>
</Pivot.HeaderTemplate>
<Pivot.ItemTemplate>
<DataTemplate x:DataType="local:TestClass">
<Grid>
<TextBlock Text="{Binding Content, Mode=OneWay}"/>
</Grid>
</DataTemplate>
</Pivot.ItemTemplate>
</Pivot>
Code behind for simulating the binding
public sealed partial class BlankPage6 : Page
{
ObservableCollection<TestClass> SampleSource = new ObservableCollection<TestClass>();
public BlankPage6()
{
this.InitializeComponent();
SampleSource.Add(new TestClass { HeaderTitle = "Test Header1", Content = "Test content 1" });
SampleSource.Add(new TestClass { HeaderTitle = "Test Header2", Content = "Test content 2" });
SampleSource.Add(new TestClass { HeaderTitle = "Test Header3", Content = "Test content 3" });
TestPivot.ItemsSource = SampleSource;
}
}
public class TestClass
{
public string HeaderTitle { get; set; }
public string Content { get; set; }
}
Output:
My LonglistSelector only displays GroupHeaderTemplate Data (ImageSource,Title) but ItemTemplate DataTemplate (SubItemTitle, Location) not displayed. How can i solve it?
public class Data
{
public string Title { get; set; }
public string ImageSource { get; set; }
public List<SubItem> SubItems { get; set; }
public Data()
{
SubItems = new List<SubItem>();
}
}
public class SubItem
{
public string SubItemTitle { get; set; }
public string Location { get; set; }
}
<phone:LongListSelector ItemsSource="{Binding DataCollection}" Grid.Row="0" IsGroupingEnabled="True">
<phone:LongListSelector.GroupHeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="10">
<Image Source="{Binding ImageSource}"/>
<TextBlock Text="{Binding Title}"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.GroupHeaderTemplate>
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding SubItemTitle}" Padding="5" FontSize="40"/>
<TextBlock Text="{Binding Location}" Padding="5" FontSize="40"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
You have to convert whatever the class you are using to group the items from inheriting. Try using the List than the IEnumerator.
This one discusses the same longlistselector issue.
Grouped LongListSelector: headers appear, items don't
Hope it helps!
This MSDN example helped me a lot when I had trouble understanding Grouping with LongListSelector
How to display data in a grouped list in LongListSelector for Windows Phone 8
It needs to be Grouped by a Key Value. Of all the of the examples I know of it's always something like this:
List<AlphaKeyGroup<your_data_type>> my_group_list; // or
ObservableCollection<AlphaKeyGroup<your_data_type>> my_group_list;
Not a List that has a property that is a SubList.
AlphaKeyGroup is just a List<T>/ObservableCollection<T> with an extra property for a Key
Think of it this way, in your code how does the LongListSelector know that your "Title" is the group key rather than the "ImageSource"?
If the code on the MSDN page is too complicated to understand, you can always take the easier route and use LINQ using the GroupBy.
Here a SO example: Group by in LINQ