Silverlight: MVVM and re-initializing form - mvvm

We use Prism and from the grid we pop up a edit form that has two options, "Save" and "Save and New". My question is about re-initializing form. I am wondering if there is a better or simpler way? What I do is expose a InteractionRequest on the view model, and than use InteractionRequestTrigger in xaml to change the properties of the form, like this:
private void SubmitAndNewCommandCallback(IEnumerable<ValidationResult> errors)
{
if (errors != null && errors.Any())
{
Errors = errors.Select(x => x.ErrorMessage).ToList();
}
else
{
if (IsNew)
{
_events.GetEvent<BidAgentCreated>().Publish(this.BidAgent);
}
_intializeFormRequest.Raise(this);
}
}
<i:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding InitializeFormRequest}" >
<ei:ChangePropertyAction TargetName="ctlAgentType" PropertyName="SelectedIndex" Value="0" />
<ei:ChangePropertyAction TargetName="ctlAgentSearchBox" PropertyName="Text" Value=""/>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>

A clean way is to get rid of the logic in your View and keep it in the ViewModel.
in xaml
<ComboBox ItemsSource="{Binding AgentTypes}" SelectedItem="{Binding SelectedAgentType,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"/>
<TextBox Text="{Binding SearchText,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
in the ViewModel
private void SubmitAndNewCommandCallback(IEnumerable<ValidationResult> errors)
{
if (errors != null && errors.Any())
{
Errors = errors.Select(x => x.ErrorMessage).ToList();
}
else
{
if (IsNew)
{
_events.GetEvent<BidAgentCreated>().Publish(this.BidAgent);
}
SearchText="";
SelectedAgentType = AgentTypes.First(); //selects first agenttype, or set to null to select nothing in the combobox
}
}

Related

Xamarin Forms Controls values not visible

I have created a page that passes a value to a new page that will allow users to update the data. When the users selects the record to be updated the edit form opens but the data is not visible. If the value is changed and the edit button clicked it will update the value, but it is never visible. How can I show the data that is to be edited?
View Model
namespace QiApp.ViewModels
{
public class EditTodayCasesViewModel
{
private SxCaseDataService _sxCaseDataService = new SxCaseDataService();
public SxCase SelectedSxCase { get; set; }
public ICommand EditSxCaseCommand => new Command(async () =>
{
await _sxCaseDataService.PutSxCase(SelectedSxCase.SxCaseId, SelectedSxCase);
});
}
}
Edit Page xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:QiApp.ViewModels;assembly=QiApp.UWP"
x:Class="QiApp.Views.EditTodayCasePage">
<ContentPage.BindingContext>
<viewModels:EditTodayCasesViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Label Text="Surgery Case"/>
<Label Text="{Binding SelectedSxCase.SxCaseId}"/>
<Entry Text="{Binding SelectedSxCase.Record}"/>
<Switch IsToggled="{Binding SelectedSxCase.Complete}"/>
<Button Text="Edit Surgery Case"
Command="{Binding EditSxCaseCommand}"/>
</StackLayout>
</ContentPage>
Code behind
namespace QiApp.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class EditTodayCasePage : ContentPage
{
public EditTodayCasePage(SxCase sxCase)
{
InitializeComponent();
var editTodayCasesViewModel = BindingContext as EditTodayCasesViewModel;
editTodayCasesViewModel.SelectedSxCase = sxCase;
}
}
}
Everything is alright except that your view gets bound to a view model which stays silent if properties are changed. Your view cannot get any information on when it should update itself and hence the UI as soon as the property SelectedSxCase gets changed.
Thankfully this can be done very easily by simply implementing the common interface INotifyPropertyChanged and extending your bound properties with a code line raising the event the interface provides.
Basically it goes like this ...
private SxCase _case;
public SxCase SelectedSxCase
{
get => _case;
set
{
_case = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedSxCase)));
}
}
... but there are several implementations to do that more elegant like using the CallerMemberName or even weaving the getter and setter automatically with Fody.

how to bind multiple selected items of syncfusion xamarin forms datagrid in mvvm?

I am able to bind SelectedItem if Selection Mode is single but if it is set to multiple then how do you bind it?
Here is what I tried for Single Selection Mode
<sync:SfDataGrid Grid.Row="1" AutoGenerateColumns="False" AllowSorting="True"
AllowGroupExpandCollapse="True" AutoExpandGroups="True"
SelectionMode="Multiple" ColumnSizer="Star"
ItemsSource="{Binding LstItems}"
SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}"
>
<sync:SfDataGrid.Columns>
<sync:GridTextColumn HeaderText="Name" MappingName="Name" />
<sync:GridTextColumn HeaderText="MRP" MappingName="MRP"/>
<sync:GridTextColumn HeaderText="Category" MappingName="Category" Width="0"/>
</sync:SfDataGrid.Columns>
<sync:SfDataGrid.GroupColumnDescriptions>
<sync:GroupColumnDescription ColumnName="Category"/>
</sync:SfDataGrid.GroupColumnDescriptions>
</sync:SfDataGrid>
In the above xaml, selection mode is set to multiple but I am unable to get the SelectedItems in xaml as mentioned here
https://help.syncfusion.com/xamarin/sfdatagrid/selection
In SfDataGrid, it is not possible to bind the SfDataGrid.SelectedItems property to the view model as like SelectedItem property since we can only get the selected items in SfDataGrid. Hence, you will not be able to bind the values in XAML for SelectedItems property.
However, you can achieve your requirement by writing behavior for SfDataGrid which will not affect the MVVM pattern. Please refer the below code snippet.
<sfGrid:SfDataGrid x:Name="dataGrid"
AutoGenerateColumns="True"
ItemsSource="{Binding OrdersInfo}"
SelectionMode="Multiple">
<b:Interaction.Behaviors>
<b:BehaviorCollection>
<b:EventToCommand Command="{Binding SelectionCommand}"
CommandParameter="{x:Reference Name=dataGrid}"
EventName="SelectionChanged" />
</b:BehaviorCollection>
</b:Interaction.Behaviors>
</sfGrid:SfDataGrid>
// In ViewModel.cs
public ViewModel()
{
selectionCommand = new Command<SfDataGrid>(onSelectionChanged);
selectedItems = new ObservableCollection<object>();
}
private Command<SfDataGrid> selectionCommand;
public Command<SfDataGrid> SelectionCommand
{
get { return selectionCommand; }
set { selectionCommand = value; }
}
private ObservableCollection<object> selectedItems;
public ObservableCollection<object> SelectedItems
{
get { return selectedItems; }
set { selectedItems = value; }
}
private void onSelectionChanged(SfDataGrid obj)
{
//you can get the selected items in the datagrid
selectedItems = obj.SelectedItems;
}
Also, we have prepared a sample for your reference and you can download the same from the below link.
Sample link: http://www.syncfusion.com/downloads/support/directtrac/168321/ze/DataGridDemo352928928
Regards,
Divakar.

How do I get the values from the cells of a selected row in a datagrid?

I see where this question has been asked but most of the answers seem to center around code behind while I am using an MVVM pattern. For those answers that pertain to my situation, I have tried a number of different suggestions but I seem to be missing the big picture.
Basically what I am looking to do is add a delete button that would delete all selected rows in the datagrid from an SQL database or add a delete button to each row. I would prefer to have a single delete button but am open to a delete button in each row, which I have actually already done but still face the same problem in that I need to delete a specific record ID from the database as well as remove it from the collection.
XAML
<DataGrid x:Name="licenseGrid"
ItemsSource="{Binding LoggedUsers}"
SelectedItem="{Binding SelectedLicense, Mode=TwoWay}"
Style="{DynamicResource DataGridStyle}"
Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="6"
Height="535"
VerticalAlignment="Top"
IsReadOnly="True"
AutoGenerateColumns="False"
HeadersVisibility="Column"
SelectionMode="Extended"
CanUserDeleteRows="True">
ViewModel
public ObservableCollection<MD_LoggedUsersModel> LoggedUsers
{
get { return _loggedUsers; }
set { _loggedUsers = value; NotifyPropertyChanged(nameof(LoggedUsers)); }
public string SelectedLicense
{
get
{
return _selectedLicense;
}
set
{
if (_selectedLicense != value)
{
_selectedLicense = value;
OnPropertyChanged(nameof(SelectedLicense));
RevokeSelected(_selectedLicense);
}
if (_selectedLicense == null)
{
LoadData();
}
}
}
The value that is being returned is the name of the LoggedUsers model.
How do I get the value of each cell?
You'll need to update your SelectedItem to be the same type as the items in your ObservableColleciton.
public MD_LoggedUsersModel SelectedLicense
{
get
{
return _selectedLicense;
}
set
{
if (_selectedLicense != value)
{
_selectedLicense = value;
}
}
}

Binding a View to my main window only shows me the type

I'm trying to bind a View to my ContentControl. Currently, it just shows me the type (eg NameSpace.ViewModel.MainWindowViewModel)
Although I will point out, I'm not sure if I'm approaching this correctly.
My simple set up is I have a View (UserControl) which is empty other than a single control (which has been placed just for the visual).
My MainWindow.xaml
<Window x:Class="DelegateGoodExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:DelegateGoodExample.ViewModel"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<viewModel:MainWindowViewModel x:Key="Vm" />
</Window.Resources>
<Grid>
<ContentControl Height="147" Margin="53,132,60,0"
VerticalAlignment="Top"
Content="{StaticResource Vm}" />
</Grid>
</Window>
(There is nothing in the code behind).
My MainWindowViewModel.cs
namespace DelegateGoodExample.ViewModel
{
public class MainWindowViewModel
{
private object _currentView;
public object CurrentView
{
get { return new View.QuickView(); }
set { _currentView = value; }
}
}
}
So, my question is,
Do I have to set a datacontext in this instance (and even if I do add it the results persist)?
What have I done wrong?
You are putting a viewmodel inside the ContentControl, not a view. Since your viewmodel class is not a UIElement and there is no DataTemplate to determine how it should be rendered, what gets displayed is simply its .ToString() representation.
An immediate fix would be:
<ContentControl Height="147" Margin="53,132,60,0"
VerticalAlignment="Top"
Content="{Binding Source={StaticResource Vm}, Path=View}" />
However, instead of doing things this way you should be putting your view inside the Grid directly, and the viewmodel should not have any knowledge of the view.

How do I use grouping using Collection View MVVM?

I'm fairly new to MVVM, and I have recently started a project cleaning up my codebehind and bit by bit I am moving everything to Model and ViewModel.
My problem is, now, how do you use grouping using Collection View without any code behind? I thought I had figured it out, after reading answers to similar questions here on Stackoverflow, but I still can't get it to work. Probably a silly mistake, but I would be very grateful if somebody could have a look at my code and let me know what they think. All feedback is great feedback, I really want to become a good programmer :)
The list is btw of the type ObservableCollection in the Menu class.
<CollectionViewSource x:Key="foods" Source="{Binding Items}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<ListBox x:Name="selectedMenuItem" Foreground="White" Grid.Column="0" Grid.Row="1" ItemsSource="{Binding Source={StaticResource foods}}"
DisplayMemberPath="Name" Background="{x:Null}" BorderThickness="0">
<ListBox.GroupStyle>
<x:Static Member="GroupStyle.Default"/>
</ListBox.GroupStyle>
</ListBox>
private CollectionViewSource _items;
private Menu _menu = new Menu();
public ICollectionView Items
{
get
{
if (_items == null)
{
_items = new CollectionViewSource {Source = new ObservableCollection<MenuItem>(_menu.MyMenu)};
}
return _items.View;
}
}
I'm assuming your problem is that data doesn't show up in your ListBox? Try programmatically adding your groupings to _items and binding your ListBox.ItemsSource directly to Items:
public ICollectionView Items
{
get
{
if (_items == null)
{
_items = new CollectionViewSource {Source = new ObservableCollection<MenuItem>(_menu.MyMenu)};
_items.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
}
return _items.View;
}
}
<ListBox x:Name="selectedMenuItem" Foreground="White" Grid.Column="0" Grid.Row="1" ItemsSource="{Binding Items}"
DisplayMemberPath="Name" Background="{x:Null}" BorderThickness="0">
<ListBox.GroupStyle>
<x:Static Member="GroupStyle.Default"/>
</ListBox.GroupStyle>
</ListBox>
You can then do away with the foods resource, assuming I haven't boffed my code.