UWP DataGrid wont update the UI when the Item Source was changed in c# - mvvm

Hi on my UWP app i use a DataGrid control (microsoft community nuget) it updates on load but when added new items it wont get update the UI. when at start the app trigger and load from the data source but after added new items it wont get refreshed even tho the ObservableCollection get changed. data add through a ContentDialog, does ContentDialog block call to the frame when get called ?
<controls:DataGrid x:Name="DataGrid1" ItemsSource="{x:Bind ViewModel.Collection,Mode=OneWay}" Foreground="White"
Grid.Row="1"
Margin="12"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"
AlternatingRowBackground="Transparent"
AlternatingRowForeground="Gray"
AreRowDetailsFrozen="False"
AutoGenerateColumns="False"
CanUserSortColumns="False"
CanUserResizeColumns="False"
CanUserReorderColumns="True"
ColumnHeaderHeight="32"
MaxColumnWidth="400"
IsReadOnly="False"
RowDetailsVisibilityMode="VisibleWhenSelected"
SelectionMode="Extended">
<controls:DataGrid.Columns>
<controls:DataGridTextColumn
x:Name="id"
Header="ID"
Width="SizeToHeader"
Binding="{Binding ID,Mode=OneWay}"
FontSize="20"
Visibility="Visible">
<controls:DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</controls:DataGridTextColumn.ElementStyle>
</controls:DataGridTextColumn>
<controls:DataGridTextColumn
Header="ID"
Width="SizeToHeader"
Binding="{Binding Number,Mode=OneWay}"
Tag="ID"
FontSize="20" />
<controls:DataGridTextColumn
Header="ID"
Width="SizeToHeader"
Binding="{Binding Name,Mode=OneWay}"
FontSize="20" />
<controls:DataGridTextColumn
Header="ID"
Width="SizeToHeader"
Binding="{Binding StartDate,Mode=OneWay}"
FontSize="20" />
<controls:DataGridTextColumn
Header="ID"
Width="SizeToHeader"
Binding="{Binding EndDate,Mode=OneWay}"
FontSize="20" />
</controls:DataGrid.Columns>
</controls:DataGrid>
</Grid>
Here is my viewmodel
class PermitsViewModel : INotifyPropertyChanged
{
//public List<PermitData> PermitsData { get; set; }
public ObservableCollection<PermitData> Collection
{
get
{
return Collection;
}
set
{
Collection = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Collection"));
}
}
}
private static Frame _EntryFrame;
public PermitsViewModel() {
//PermitsData = new List<PermitData> {
//new PermitData { ID = 9966615, Number = 78, Name = "Mark", StartDate = new DateTime(2016, 8, 30), EndDate = new DateTime(2017, 5, 30) },
//new PermitData { ID = 9945512, Number = 79, Name = "Steve", StartDate = new DateTime(2019, 7, 30), EndDate = new DateTime(2015, 9, 30) }
//};
Collection = new ObservableCollection<PermitData>();
Collection.Add(new PermitData { ID = 22, Number = 22, Name = "KASUN", StartDate = new DateTime(2016, 8, 30), EndDate = new DateTime(2016, 8, 30) });
OnPropertyChanged();
}
public void AddToTable(int ID, int Number, string Name, DateTime StDate, DateTime EndDate)
{
//Collection = new ObservableCollection<PermitData>();
Collection.Add(new PermitData { ID = ID, Number = Number, Name = Name, StartDate = new DateTime(2016, 8, 30), EndDate = new DateTime(2016, 8, 30) });
Debug.WriteLine(Collection[0]);
//OnPropertyChanged();
}
}
Data property layer
class PermitData
{
public int ID { get; set; }
public int Number { get; set; }
public string Name { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
main
public sealed partial class Permits : Page
{
PermitsViewModel ViewModel { get; } = new PermitsViewModel();
public Permits()
{
this.InitializeComponent();
ViewModel.Init(DataEntryPort);
base.DataContext = ViewModel;
}
}

Related

Blazorise DataGrid DataGridSelectColumn not recording change

I am trying to add select in blazorise component. Somehow it not showing me dropdown selected values. Please go through below code
Temp.razor file
#page "/temp"
#using Blazorise.DataGrid
<h3>TempComponent</h3>
<DataGrid TItem="ClassA"
Data="#Classes"
Editable="true"
RowUpdated="#OnRowUpdatedAsync">
<DataGridCommandColumn >
<EditCommandTemplate>
<Blazorise.Button Clicked="#context.Clicked"><Icon Name="IconName.Edit" /></Blazorise.Button>
</EditCommandTemplate>
<SaveCommandTemplate>
<Blazorise.Button Clicked="#context.Clicked"><Icon Name="IconName.Save" /></Blazorise.Button>
</SaveCommandTemplate>
</DataGridCommandColumn>
<DataGridColumn TItem="ClassA" Field="#nameof(ClassA.Name)" Caption="Name" Editable="true"/>
<DataGridSelectColumn TItem="ClassA" Field="#nameof(ClassA.B)" Caption="B" Editable="true">
<DisplayTemplate>
#if (#context.B != null)
{
#context.B.Name
}
</DisplayTemplate>
<EditTemplate>
<Select TValue="int" SelectedValue="#selectValue"
SelectedValueChanged="#SelectedValueChangedHandler">
#if (ClassesB != null)
{
foreach (var classB in ClassesB)
{
<SelectItem Value="#(classB.Id)">#(classB.Name)</SelectItem>
}
}
</Select>
</EditTemplate>
</DataGridSelectColumn>
</DataGrid>
#code{
int selectValue = 0;
public class ClassA {
public string Name { get; set; }
public ClassB B { get; set;}
}
public class ClassB {
public int Id { get; set; }
public string Name { get; set; }
}
List<ClassA> Classes = new List<ClassA> {
new ClassA { Name = "Class1", B = new ClassB() { Id = 1, Name = "ClassB1" }},
new ClassA { Name = "Class2", B = new ClassB() { Id = 2, Name = "ClassB2" }}
};
List<ClassB> ClassesB = new List<ClassB> {
new ClassB { Id = 1, Name = "ClassB1" },
new ClassB { Id = 2, Name = "ClassB2" }
};
protected void OnRowUpdatedAsync(SavedRowItem<ClassA, Dictionary<string, object>> e)
{
}
private void SelectedValueChangedHandler(int value)
{
Console.WriteLine("values " + value);
selectValue = value;
}
}
And Current Output Screenshot (select dropdown value not showing )
Whenever user select column then value should be change in list and also display in datagrid.
For this I took reference from https://github.com/Megabit/Blazorise/issues/561
Please help me to solve this issue .
Thanks in advance

Xamarin forms List in List (navigating using prism)

The issue is that i cant seem to pass through the NavigationService to ModulesModel to use to Navigate to AnotherView items that i can only pass it to SectionModel. but i can only pass the NavigationService to the Parent ListView(Which i dont want) it needs to pass through the child ListView. HELP!!!
UI - CODE
<ListView
SeparatorColor="Transparent"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
ItemsSource="{Binding Data}"
HasUnevenRows="True">
<ListView.Header>
<local:ModuleCourseHeaderViewControl />
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame
HasShadow="False"
BackgroundColor="{StaticResource BiancaBackgroundColour}">
<Frame.CornerRadius>
<OnIdiom
x:TypeArguments="x:Single"
Phone="0"
Tablet="15" />
</Frame.CornerRadius>
<Frame.Margin>
<OnIdiom
x:TypeArguments="Thickness"
Phone="0,10"
Tablet="20,10" />
</Frame.Margin>
<StackLayout>
<Label
FontFamily="{StaticResource LatoRegularFont}"
TextColor="{StaticResource MediumGold}"
Text="{Binding Title}">
<Label.FontSize>
<OnIdiom
x:TypeArguments="x:Double"
Phone="15"
Tablet="17" />
</Label.FontSize>
</Label>
<flv:FlowListView
BackgroundColor="Transparent"
SeparatorVisibility="None"
HasUnevenRows="False"
FlowColumnCount="{DynamicResource ModuleFlowColumnCount}"
RowHeight="{DynamicResource RiseCardHeight}"
FlowItemsSource="{Binding Modules}">
<flv:FlowListView.FlowColumnTemplate>
<OnIdiom
x:TypeArguments="DataTemplate">
<OnIdiom.Phone>
<DataTemplate>
<local:ModuleCourseCardPhoneControl>
<local:ModuleCourseCardPhoneControl.GestureRecognizers>
<TapGestureRecognizer
CommandParameter="{Binding}"
Command="{Binding ItemSelectedCommand}" />
</local:ModuleCourseCardPhoneControl.GestureRecognizers>
</local:ModuleCourseCardPhoneControl>
</DataTemplate>
</OnIdiom.Phone>
<OnIdiom.Tablet>
<DataTemplate>
<local:ModuleCourseCardControl>
<local:ModuleCourseCardControl.GestureRecognizers>
<TapGestureRecognizer
CommandParameter="{Binding}"
Command="{Binding ItemSelectedCommand}" />
</local:ModuleCourseCardControl.GestureRecognizers>
</local:ModuleCourseCardControl>
</DataTemplate>
</OnIdiom.Tablet>
</OnIdiom>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Models - CODE
public class NewModuleModel
{
public IList<SectionModel> Sections { get; set; }
public int ActiviyId { get; set; }
public string CourseTitle { get; set; }
}
public class SectionModel
{
public string Title { get; set; }
public IList<ModulesModel> Modules { get; set; }
public INavigationService NavigationService { get; set; }
}
public class ModulesModel
{
public int ModuleId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string ModuleImageUrl { get; set; }
public double Progress { get; set; }
public double RoundedProgress
{
get
{
var p = Math.Round(Progress * 100, 0);
return p;
}
}
public DateTime? LastActivityDTUtc { get; set; }
public DateTime? CompletedDate { get; set; }
public string EstimatedDuration { get; set; }
public int AssetCount { get; set; }
public int? NavigationAssetID { get; set; }
public int ActiviyId { get; set; }
public string CourseTitle { get; set; }
public int? NavigationAssetContentType { get; set; }
public string Status
{
get
{
var result = string.Empty;
if (CompletedDate != null)
{
var c = String.Format("{0:d MMMM yyyy}", CompletedDate);
result = "Completed on " + c;
}
else if (LastActivityDTUtc != null)
{
var l = String.Format("{0:d MMMM yyyy}", LastActivityDTUtc);
result = "Last Activity on " + l;
}
else
{
result = "New";
}
return result;
}
}
public ModulesModel()
{
ItemSelectedCommand = new DelegateCommand<ModulesModel>(this.OnCardItemSelectedCommand);
}
private void OnCardItemSelectedCommand(ModulesModel module)
{
if (module.NavigationAssetContentType == 28 && module.AssetCount == 1)
{
var navigationParams = new NavigationParameters();
navigationParams.Add("AssetContentId", module.NavigationAssetID);
navigationParams.Add("AssetActivityId", module.ActiviyId);
navigationParams.Add("AssetModuleId", module.ModuleId);
navigationParams.Add("ParamCourseTitle", module.CourseTitle);
navigationParams.Add("ParamModuleTitle", module.Title);
Preferences.Set("NavFromModulePage", true);
NavigationService.NavigateAsync("AssetWebViewPage", navigationParams, true, false).Forget();
}
else
{
var navigationParams = new NavigationParameters();
navigationParams.Add("ParamModuleId", module.ModuleId);
navigationParams.Add("ParamActivityId", module.ActiviyId);
navigationParams.Add("ParamCourseTitle", module.CourseTitle);
Preferences.Set("NavFromModulePage", false);
//NavigationService.NavigateAsync("ModuleAssetPage", navigationParams, true, false);
}
}
public DelegateCommand<ModulesModel> ItemSelectedCommand { get; set; }
}
}
ViewModel - CODE
private async void LoadData()
{
this.ExecuteAsyncTask(async () =>
{
var result = await this.ModuleService.GetNewModuleAsync(ActivityID);
if (result != null)
{
CourseTitle = result.CourseTitle;
CourseDescription = result.CourseDescription;
CourseImageUrl = result.CourseImageUrl;
DurationInSeconds = result.DurationInSeconds;
DurationAsReadableString = result.DurationAsReadableString;
DisplayDescription = result.DisplayDescription;
Progress = Math.Round(result.Progress * 100, 0);
StartDateUTC = result.StartDateUTC;
EnrolmentDate = result.EnrolmentDate;
LastActivityDate = result.LastActivityDate;
foreach (var item in result.Sections)
{
var itemToAdd = new SectionModel
{
Title = item.Title,
Modules = item.Modules,
NavigationService = navigationService
};
Device.BeginInvokeOnMainThread(() =>
{
this.Data.Add(itemToAdd);
});
}
}
else
{
var exception = new Exception($"Api Error");
}
});
}
}
Remember that MVVM is Model-View-ViewModel. Very often we see people confusing Models and ViewModel's. I'll more indirectly answer your question by showing you the proper architecture. To start we'll look at a ToDoItem model.
public class ToDoItem
{
public string Name { get; set; }
public DateTime Created { get; set; }
public DateTime Due { get; set; }
}
With this note that we can make this easier to Bind by having our model inherit from something like Prism's BindableBase or ReactiveUI's ReactiveObject. I will make the assumption here that you already understand how to make those properties implement INotifyPropertyChanged.
The next thing we need is our ViewModel, we'll say that this is the ToDoItemsPageViewModel
public class ToDoItemsPageViewModel : BindableBase
{
public ObservableCollection<ToDoItem> Items { get; set; }
public DelegateCommand<ToDoItem> ItemSelectedCommand { get; }
private async void ItemSelectedCommandExecuted(ToDoItem item)
{
// do your navigation here..
}
}
Finally we have our ListView or CollectionView (which is what you should use as ListView is being deprecated). For simplicity let's look at the following.
<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}" />
<Button Text="Show"
Command="{Binding ItemSelectedCommand}"
CommandParameter="{Binding .}" />
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The core idea to understand here is that the ListView shares the BindingContext of its parent Page. Within our DataTemplate our BindingContext is a ToDoItem and not the ToDoItemsPageViewModel. As a result our binding for the value of Name will return the name of the individual ToDoItem. While our Binding for the CommandParameter will pass in the entire ToDoItem, our Binding for the Command will not work because the ToDoItem has no such command.
Now let's look at how we can access the Command from our ViewModel.
<ListView ItemsSource="{Binding Items}" x:Name="list">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}" />
<Button Text="Show"
Command="{Binding BindingContext.ItemSelectedCommand,Source={x:Reference list}}"
CommandParameter="{Binding .}" />
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
What we've done here are three small changes.
We've added an x:Name to the ListView. This could also be put on the parent Page.
We've changed the Command Binding from ItemSelectedCommand to BindingContext.ItemSelectedCommand
We've changed the Command Binding to include a Source which references the parent item (the list in this case)
What this accomplishes is to update that one specific binding to look at the parent. This effectively sets the BindingContext (for this single property) to the actual parent. In this case you can think of it as the BindingContext being set to the instance of the ListView. This means that our Binding must reference the BindingContext, and then we can dot into access a property of our BindingContext on the ListView.

How can I Binding for Items source using object?

I can't Binding.
and show data Object such as MyProject.Models.Class
`XAML
<Picker ItemsSource="{Binding City}" x:Name="selectCity" />`
`C#`
`List<AddInfo> addInfo = new List<AddInfo>`
` {
new Class{City ="West Amman" },
new Class{City ="East Amman" },
new Class{City ="Ajloun" }
};`
`MyProject.Model.Class`
`public class Class{
public string City { get; set; }
}`
`Output`
`MyProject.Model.Class
MyProject.Model.Class
MyProject.Model.Class`
XAML
<Picker x:Name="picker" Title="Select Country" ItemDisplayBinding="{Binding Name}" >
</Picker>
Use this in your view model
C#
public partial class SamplePage: ContentPage
{
public ObservableCollection<Country> CountryList { get; set; } = new ObservableCollection<Country>
{
new Country{Name = "India" },
new Country{Name = "Australia" },
new Country{Name = "UAE" },
new Country{Name = "USA" },
};
public SamplePage()
{
InitializeComponent();
picker.ItemsSource = CountryList;
}
}
Country class
public class Country
{
public string Name { get; set; }
}

mvvm datagrid cannot update model from view

I'm new to WPF, I am using MVVM via MVVMLight, I have a datagrid bound to a ObservableCollection of view models, When I manually update modelView, the view is changed as expected,
the problem is that when I change view, the viewModel's value is not changed,
this is model:
public class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
this is viewModel:
public class StudentViewModel : ViewModelBase
{
private Student _student;
public StudentViewModel(Student student)
{
_student = student;
}
public string FirstName
{
get { return _student.FirstName; }
set
{
if(_student.FirstName==value)return;
_student.FirstName = value;
RaisePropertyChanged("FirstName");
}
}
public string LastName
{
get { return _student.LastName; }
set
{
if(_student.LastName==value)return;
_student.LastName = value;
RaisePropertyChanged("LastName");
}
}
}
this is Test.xaml:
<DataGrid Name="DgrdColumns" Grid.Row="1" Grid.Column="0" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="FirstName">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding FirstName, Mode=TwoWay}"></TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="LastName" Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding LastName, Mode=TwoWay}"></TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
this is bind data code:
void BindData()
{
var students = new List<Student>()
{
new Student()
{
FirstName = "aa",
LastName = "AA"
},
new Student()
{
FirstName = "bb",
LastName = "BB"
}
};
var viewModels=new ObservableCollection<StudentViewModel>(students.Select(x=>new StudentViewModel(x)));
DgrdColumns.ItemsSource = viewModels;
}
Please try to set UpdateSourceTrigger in your bindings:
<TextBox Text="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
Let me know if it still doesn't work.

Xceed Datagrid loses SelectedItem when child selected

I have an Xceed datagrid in a WPF MVVM application which is set up to hold master-detail records. When a child row is selected, I want the ViewModel to detect the selected child. I would like to do this preferably with zero code-behind. I have written code which executes an action on the selected item when a contextmenu is clicked. This works correctly when a parent is selected, but always returns null when a child is selected.
I have put together a very simplified version of what I am trying to acheive:
My XAML is:
<Window x:Class="MasterDetailSelection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xcdg="clr-namespace:Xceed.Wpf.DataGrid;assembly=Xceed.Wpf.DataGrid.v4.2"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<xcdg:DataGridCollectionViewSource x:Key="cvs_parents" Source="{Binding Path=Parents}">
<xcdg:DataGridCollectionViewSource.DetailDescriptions>
<xcdg:PropertyDetailDescription RelationName="Children"
AutoCreateDetailDescriptions="False">
</xcdg:PropertyDetailDescription>
</xcdg:DataGridCollectionViewSource.DetailDescriptions>
</xcdg:DataGridCollectionViewSource>
</Grid.Resources>
<xcdg:DataGridControl x:Name="ParentGrid"
NavigationBehavior="RowOnly"
ItemsSource="{Binding Source={StaticResource cvs_parents}}"
SelectedItem="{Binding SelectedItem}"
AutoCreateDetailConfigurations="True"
ReadOnly="True">
<xcdg:DataGridControl.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Execute Command"
CommandParameter="{Binding DataContext.SelectedItem}"
Command="{Binding DataContext.SampleCommand}" />
</ContextMenu>
</xcdg:DataGridControl.ContextMenu>
</xcdg:DataGridControl>
</Grid>
</Window>
The View Model is:
namespace MasterDetailSelection
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Microsoft.Practices.Composite.Presentation.Commands;
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<Parent> _parents;
public event PropertyChangedEventHandler PropertyChanged;
private DelegateCommand<Object> _sampleCommand;
private object _selectedItem;
public ObservableCollection<Parent> Parents
{
get { return _parents; }
set
{
_parents = value;
OnPropertyChanged("Parents");
}
}
public DelegateCommand<Object> SampleCommand
{
get
{
if (_sampleCommand == null)
{
_sampleCommand = new DelegateCommand<object>(ExecuteSampleCommand, CanExecuteSampleCommand);
OnPropertyChanged("SampleCommand");
}
return _sampleCommand;
}
}
public bool CanExecuteSampleCommand(Object commandParameter)
{
return true;
}
public void ExecuteSampleCommand(Object commandParameter)
{
Console.WriteLine("ExecuteSampleCommand");
}
public object SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem != value)
{
_selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
}
public void LoadParents()
{
var parents = new ObservableCollection<Parent>()
{
new Parent()
{
Id=1,
Description = "Parent1",
Children = new List<Child>(){new Child() {Id = 1, Description = "Child1"} }
}
};
Parents = parents;
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
There are 2 simple entities:
public class Parent
{
public int Id { get; set;}
public string Description { get; set;}
public IEnumerable<Child> Children { get; set;}
}
public class Child
{
public int Id { get; set; }
public string Description { get; set; }
}
The OnStartup override in App.xaml.cs contains the following:
var viewModel = new ViewModel();
var window = new MainWindow();
window.DataContext = viewModel;
viewModel.LoadParents();
window.Show();
Whenever i select the parent row, the SelectedItem setter is called with a populated object. When I selected a child row, the same setter is called, but with a null value.
Is there any way I can get a reference to the selected item when the context menu is clicked on a child row - and do this without code-behind. If not, is it possible with code-behind?
Perhaps you should set a context menu directly on the cells or row like below. Then you can send the appropriate model to the command. In the example below I use a static locator class that has my VM to bind the actual command too.
<Style TargetType="{x:Type xcdg:DataCell}">
<Setter Property="ContextMenu" Value="{StaticResource CellContextMenu}">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Execute Command"
CommandParameter="{Binding}"
Command="{Binding Source={StaticResource Locator} Path=ViewModel.SampleCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>