I am trying to add DataTemplate XAML code dynamically for CarouselView from View Model in MAUI. I am getting error. Please refer the attached images - xamarin.forms

Refer the below code snippets,
I am having Frame observable collection in View Model and it is binding to the UI.
In UI, I have CarouselView and assigning the DataTemplate values with ViewModel Property.
**ViewModel Property:**
public ObservableCollection<Frame> CarouselViewItemSource
{
get => carouselViewItemSource;
set
{
SetProperty(ref carouselViewItemSource, value);
}
}

Related

Applying filtering to ObservableCollection items without allocating additional memory for each filtered item

.Net Maui app. I want to have a SearchBar where user is able to enter search text and ListView or any other view with ItemsSource to react immediately by filtering out any not matched item. I do not want to re-create ObservableCollection of items each time user changes one single symbol in the search bar since it re-allocates memory for the filtered list of items, and the list could be long.
I've found how this handled by syncfuntion list control: https://help.syncfusion.com/maui/listview/filtering. Filter there applied "on the run" to each item in the list while binding it to the ItemsSource of a View, so no new collection is created for the search params, same collection is used but with different param on "per item" bases. You can achieve similar effect if you use IEnumerable with custom Where statement, but then you lost benefits of your collection being "observable". Any ideas on how to resolve this without syncfusion's listview control?
"Filter" by binding IsVisible of ItemTemplates view to a bool that you set.
<ListView.ItemTemplate>
<DataTemplate>
<SomeLayoutOrViewHere IsVisible="{Binding Show}" ...
...
public ObservableCollection<MyItem> MyItems ...
void ApplyFilter(...)
{
foreach (MyItem item in MyItems)
{
item.Show = ...;
}
}
public class MyItem : ObservableObject
{
public bool Show { get => show; set => SetProperty(ref show, value); }
private bool show;
...
}

How to change property value of a view inside CarouselView's ItemTemplate?

I have a CarouselView with ItemTemplate containing a Label and a Grid. Outside of that CarouselView, I want to make a button to modify Carousel's current item's Grid's visibility. Because it's inside ItemTemplate, I can't use x:Name to refer to that specific Grid, so how can I refer to the current item's Grid so I can change its property value? Thank you.
You will want to do that through databinding. As you already mentioned, you can't use x:Name. This is because you're inside of a template. The value in x:Name would be duplicated for each time that template is applied to a concrete item in your list, in this case a CarouselView. Moreover; if you use virtualization for that list, a template might not even exist at all at that point in time. All reasons why you can't use x:Name to reference anything inside of a template.
I don't have any info about the code you want to use this with, so I'll make something up.
If the backing collection for your CarouselView is a ObservableCollection<MyItem>, then your CarouselView might look something like this:
<!-- Databinding scope here is MyViewModel -->
<CarouselView ItemsSource="{Binding MyItemsCollection}">
<CarouselView.ItemTemplate>
<!-- Databinding scope here is MyItem -->
<DataTemplate>
<Button Text="Delete" IsVisible="{Binding CanDelete}" />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
So you will have a backing view model which has a MyItemsCollection, and your page (that holds the CarouselView) has set the BindingContext to a new instance of MyViewModel:
public class MyViewModel
{
public ObservableCollection<MyItem> MyItemsCollection { get; set; } = new ObservableCollection<MyItem>();
private void LoadData()
{
var items = _myItemService.GetItems();
foreach (var item in items)
{
MyItemsCollection.Add(item);
}
}
}
Whenever you want to influence the IsVisible you will want to set the CanDelete of the MyItem that it's about to false. Let's assume MyItem looks like this:
public class MyItem
{
public bool CanDelete { get; set; }
}
You will need to implement the INotifyPropertyChanged interface on it so that the UI will pick up on any changes that are made to property values.
Now whenever you set the CanDelete of a certain instance of MyItem to false, that will change your UI. E.g.: MyItemsCollection[3].CanDelete = false;
On my YouTube channel I added a playlist with videos about data binding which might help in cases like these.
PS. At the time of writing IsVisible is bugged in .NET MAUI

Why don't my ValidateEntity errors show up in my view?

I've got some POCO Model classes that I've setup for use with the Entity Framework. I do some validation in my DbContext's ValidateEntity override. I return a DbEntityValidationResult from the ValidateEntity function, and I can see that during run-time I do add some DbValidationErrors. I can even see those errors inside of the ModelState inside of my Controller function, using the following code ...
catch (DbEntityValidationException ex)
{
foreach (var entity in ex.EntityValidationErrors)
{
foreach (var error in entity.ValidationErrors)
{
ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
}
}
}
But for some reason those errors don't show up for the desired property name in the Razor view. I use a view model that looks like the following ...
public class CharacterCreateModel
{
private Character m_character;
#region Properties
public Character Character
{
get
{
return m_character;
}
set
{
m_character = value;
}
}
#endregion
}
And in my Razor view, which is strongly typed using this CharacterCreateModel view mode, I just use the standard #Html.TextBoxFor, etc.
Validation errors coming from the Character model properly display, but validation errors from the ValidateEntity function are not showing up for that property name.
Any idea why not?
You need to include the ValidationMessageFor helper in your code to show model level properties.
You should have
#Html.EditorFor(model => model.Character)
#Html.ValidationMessageFor(model => model.Character)
If this in not showing the errors change the
#Html.ValidationSummary(true)
at the top of your view to
#Html.ValidationSummary(false)
so you can see all the validation errors and make sure they're actually being added correctly.

Silverlight Grid loaded but does not show data

I have a silverlight form where in i am populating a datagrid in the constructor of the form, below is the code...
public partial class ManageArtists : UserControl
{
ChinookDomainContext cdContext = new ChinookDomainContext();
public ManageArtists()
{
InitializeComponent();
cdContext.Load(cdContext.GetArtistsQuery());
dpArtistPager.Source = cdContext.Artists.OrderBy(artist => artist.Name);
dgArtistList.ItemsSource = cdContext.Artists.OrderBy(artist => artist.Name);
}
}
Now the problem is....Even though the data is loaded in the grid it does not show anything until i click on the Header fields of the grid. I dont understand why is this happening ??
can someone explain me whats happening !!
thank you
In this line -
dgArtistList.ItemsSource = cdContext.Artists.OrderBy(artist => artist.Name);
you'll find that your ItemsSource is being set to an instance of IOrderedEnumerable,..which doesn't support property changed notification.
To fix this quickly and easily wrap that collection in an ObservableCollection and your data should display correctly.
HTH

MVVM load data during or after ViewModel construction?

My generic question is as the title states, is it best to load data during ViewModel construction or afterward through some Loaded event handling?
I'm guessing the answer is after construction via some Loaded event handling, but I'm wondering how that is most cleanly coordinated between ViewModel and View?
Here's more details about my situation and the particular problem I'm trying to solve:
I am using the MVVM Light framework as well as Unity for DI. I have some nested Views, each bound to a corresponding ViewModel. The ViewModels are bound to each View's root control DataContext via the ViewModelLocator idea that Laurent Bugnion has put into MVVM Light. This allows for finding ViewModels via a static resource and for controlling the lifetime of ViewModels via a Dependency Injection framework, in this case Unity. It also allows for Expression Blend to see everything in regard to ViewModels and how to bind them.
So anyway, I've got a parent View that has a ComboBox databound to an ObservableCollection in its ViewModel. The ComboBox's SelectedItem is also bound (two-way) to a property on the ViewModel. When the selection of the ComboBox changes, this is to trigger updates in other views and subviews. Currently I am accomplishing this via the Messaging system that is found in MVVM Light. This is all working great and as expected when you choose different items in the ComboBox.
However, the ViewModel is getting its data during construction time via a series of initializing method calls. This seems to only be a problem if I want to control what the initial SelectedItem of the ComboBox is. Using MVVM Light's messaging system, I currently have it set up where the setter of the ViewModel's SelectedItem property is the one broadcasting the update and the other interested ViewModels register for the message in their constructors. It appears I am currently trying to set the SelectedItem via the ViewModel at construction time, which hasn't allowed sub-ViewModels to be constructed and register yet.
What would be the cleanest way to coordinate the data load and initial setting of SelectedItem within the ViewModel? I really want to stick with putting as little in the View's code-behind as is reasonable. I think I just need a way for the ViewModel to know when stuff has Loaded and that it can then continue to load the data and finalize the setup phase.
Thanks in advance for your responses.
For events you should use the EventToCommand in MVVM Light Toolkit. Using this you can bind any event of any ui element to relaycommand. Check out his article on EventToCommand at
http://blog.galasoft.ch/archive/2009/11/05/mvvm-light-toolkit-v3-alpha-2-eventtocommand-behavior.aspx
Download the sample and have a look. Its great. You won't need any codebehind then. An example is as follows:
<Page x:Class="cubic.cats.Wpf.Views.SplashScreenView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="SplashScreenPage">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand Command="{Binding LoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<Label Content="This is test page" />
</Grid>
</Page>
and the view mode could be like this
public class SplashScreenViewModel : ViewModelBase
{
public RelayCommand LoadedCommand
{
get;
private set;
}
/// <summary>
/// Initializes a new instance of the SplashScreenViewModel class.
/// </summary>
public SplashScreenViewModel()
{
LoadedCommand = new RelayCommand(() =>
{
string a = "put a break point here to see that it gets called after the view as been loaded";
});
}
}
if you would like the view model to have the EventArgs, you can simple set PassEventArgsToCommand to true:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding LoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
and the view model will be like
public class SplashScreenViewModel : ViewModelBase
{
public RelayCommand<MouseEventArgs> LoadedCommand
{
get;
private set;
}
/// <summary>
/// Initializes a new instance of the SplashScreenViewModel class.
/// </summary>
public SplashScreenViewModel()
{
LoadedCommand = new RelayCommand<MouseEventArgs>(e =>
{
var a = e.WhateverParameters....;
});
}
}
The following solution is similar to the one already provided and accepted, but it does not use a command in the view model to load the data, but a "normal method".
I think commands are more suited for user actions (commands can be available and not available at runtime), that is why a use a regular method call, but also by setting a an interaction trigger in the view.
I suggest this:
Create a view model class.
Instantiate the view model class within the xaml of the view by creating it inside the DataContext property.
Implement a method to load the data in your view model, e.g. LoadData.
Set up the view, so that this method is called when the view loads.
This is done by an interaction trigger in your view which is linked to the method in the view model (references to "Microsoft.Expression.Interactions" and "System.Windows.Interactivity" are needed):
View (xaml):
<Window x:Class="MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test"
xmlns:viewModel="clr-namespace:ViewModels"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
>
<Window.DataContext>
<viewModel:ExampleViewModel/>
</Window.DataContext>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<ei:CallMethodAction TargetObject="{Binding}" MethodName="LoadData"/>
</i:EventTrigger>
</i:Interaction.Triggers>
This will call the LoadData method in the ViewModel at runtime when the view is loaded. This is where you load your data.
public class ExampleViewModel
{
/// <summary>
/// Constructor.
/// </summary>
public ExampleViewModel()
{
// Do NOT do complex stuff here
}
public void LoadData()
{
// Make a call to the repository class here
// to set properties of your view model
}
If the method in the repository is an async method, you can make the LoadData method async too, but this is not needed in each case.
By the way, generally I would not load data in the constructor of the view model.
In the example above the (parameter less) constructor of the view model is called when the designer shows your view. Doing complex things here can cause errors in the designer when showing your view (for the same reason I would not make complex things in the views constructor).
In some scenarios code in the view models constructor can even cause issues at runtime, when the view models constructors executes, set properties of the view model which are bound to elements in the view, while the view object is not completely finished creating.
Ok, then. :-)
You can bind to a method in the ViewModel by using a behavior.
Here is a link that will help you with that.
http://expressionblend.codeplex.com/
I decided to just have the XAML declaratively bound to a Loaded event handler on the View's code-behind, which in turn just called a method on the ViewModel object, via the View's root element UserControl DataContext.
It was a fairly simple, straight forward, and clean solution. I guess I was hoping for a way to bind the Loaded event to the ViewModel object in the same declarative way you can with ICommands in the XAML.
I may have given Klinger the official answer credit, but he posted a comment to my question, and not an answer. So I at least gave him a one-up on his comment.
I had this same problem when dealing with messages between a parent window and a child window. Just change the order in which your view models are created in your ViewModelLocator class. Make sure that all view models which are dependent on a message are created before the view model that sends the message.
For example, in your ViewModelLocator class's constructor:
public ViewModelLocator()
{
if (s_messageReceiverVm == null)
{
s_messageReceiverVm = new MessageReceiverVM();
}
if (s_messageSenderVm == null)
{
s_messageSenderVm = new MessageSenderVM();
}
}

Resources