I have a outofmemoryexception . I am trying to retrieve from CRM over 7000 accounts and stock into the list of contratvuemodele. This list will be add to the observablecollection of contratvuemodele and bind to datagrid.
But my application do not work.
here is my codes:
code xaml:
<UserControl x:Class="GestionDeContrats_Offres_ClientsGUI.VueModele.UserControlGestionContrat"
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"
mc:Ignorable="d"
x:Name="GestionContrat"
xmlns:local="clr-namespace:GestionDeContrats_Offres_ClientsGUI.VueModele"
d:DesignHeight="300" >
<!-- <UserControl.DataContext>
<local:GestionDeContratVueModele />
</UserControl.DataContext>
-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="40"/>
<RowDefinition />
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="320"/>
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<TextBox Name="searchtexbox" Grid.Column="0"/>
<Image Grid.Column="1" Source="/GestionDeContrats_Offres_ClientsGUI;component/Images/16_find.gif" />
</Grid>
<ToolBar Grid.Row="1" Name="toolbarcontrat">
<Button Name="btNewContrat" Click="btNewContrat_Click">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="/GestionDeContrats_Offres_ClientsGUI;component/Images/plusvert.jpg" />
<Label Content="Nouveau" Grid.Column="1"/>
</Grid>
</Button>
<Button Name="btCopierContrat" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="/GestionDeContrats_Offres_ClientsGUI;component/Images/editcopy.png" />
<Label Content="Copier" Grid.Column="1"/>
</Grid>
</Button>
<Button Name="btSupprimerContrat" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="/GestionDeContrats_Offres_ClientsGUI;component/Images/delgreen16.jpg" />
<Label Content="Supprimer" Grid.Column="1"/>
</Grid>
</Button>
<Button Name="btModifierContrat" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Source="/GestionDeContrats_Offres_ClientsGUI;component/Images/ico_18_4207.gif" />
<Label Content="Modifier" Grid.Column="1"/>
</Grid>
</Button>
</ToolBar>
<DataGrid Name="listViewContrat" Grid.Row="2" ScrollViewer.VerticalScrollBarVisibility="Auto" ItemsSource="{Binding Path=Lescontrats, Mode=OneWay}" IsSynchronizedWithCurrentItem="True" AutoGenerateColumns="False" CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserSortColumns="True" CanUserAddRows="True" CanUserDeleteRows="True">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Nom du contrat" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=NOMDUCONTRAT, Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Date de début" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DATEDEDEBUT, Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Date de fin" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DATEDEFIN, Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Statut" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=LESTATUT,Mode=OneWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Statut avant" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text=""/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="3" Orientation="Horizontal">
<Label Margin="2" Content=""/>
<Button Content="Suivant" Name="btNext" Margin="2" />
<Button Content="Précédent" Name="btPrevious" Margin="2" />
</StackPanel>
</Grid>
</UserControl>
code cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GestionDeContrats_Offres_ClientsGUI.VueModele;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.ComponentModel;
using GestionDeContrats_Offres_Clients.GestionOffres;
using GestionDeContrats_Offres_Clients.GestionContrats;
using System.Windows.Input;
using GestionDeContrats_Offres_Clients.GestionModele;
using GestionDeContrats_Offres_ClientsGUI.crm;
using System.Data;
namespace GestionDeContrats_Offres_ClientsGUI.VueModele
{
/// <summary>
///
/// </summary>
public class GestionDeContratVueModele : VueModeleBase
{
private readonly ObservableCollection<ContratVueModele> contrats;
private readonly ICollectionView collectionView;
//private readonly PagingCollectionView pagingView;
private GestionDeContrat gestiondecontrat;
private ICommand activerContratCommand;
private ICommand supprimerContratCommand;
private ICommand modifierContratCommand;
private ICommand copierContratCommand;
private ICommand creerContratCommand;
private ICommand nextPageCommand;
private ICommand previousPageCommand;
private int currentpage;
/// <summary>
/// Constructeur de la classe
/// GestionDeContratVueModele
/// </summary>
public GestionDeContratVueModele() {
try
{
this.gestiondecontrat = new GestionDeContrat();
List<ContratVueModele> maliste = new List<ContratVueModele>(10000);
//maliste.Clear();
// this.contrats.Clear();
foreach (contract contrat in this.gestiondecontrat.ListeDeContrat())
{
// this.contrats.Add(new ContratVueModele());
maliste.Add(new ContratVueModele() { NOMDUCONTRAT = contrat.title, DATEDEDEBUT = contrat.activeon.Value, DATEDEFIN = contrat.expireson.Value, LESTATUT = contrat.statecode.formattedvalue, LESTATUTAVANT = contrat.access_etatavant.name });
// this.contrats.Add(new ContratVueModele() { NOMDUCONTRAT = contrat.title, DATEDEDEBUT = contrat.activeon.Value, DATEDEFIN = contrat.expireson.Value, LESTATUT = contrat.statecode.formattedvalue,LESTATUTAVANT=contrat.access_etatavant.name });
}
this.contrats = new ObservableCollection<ContratVueModele>(maliste);
this.collectionView = CollectionViewSource.GetDefaultView(this.contrats);
if (this.collectionView == null)
throw new NullReferenceException("collectionView");
// this.collectionView.CurrentChanged += new EventHandler(collectionView_CurrentChanged);
// this.pagingView = new PagingCollectionView(this.contrats, 15);
//if (this.pagingView == null)
//throw new NullReferenceException("pagingView");
//this.currentpage = this.pagingView.CurrentPage;
//this.pagingView.CurrentChanged += new EventHandler(pagingView_CurrentChanged);
}
catch (System.Web.Services.Protocols.SoapException soapEx)
{
soapEx.Detail.OuterXml.ToString();
}
catch (System.OutOfMemoryException memoryException)
{
memoryException.InnerException.ToString();
}
catch (System.Reflection.TargetInvocationException targetex) {
targetex.InnerException.ToString();
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
//void pagingView_CurrentChanged(object sender, EventArgs e)
//{
// OnPropertyChanged("SelectedContrat");
// Dispose();
// //throw new NotImplementedException();
//}
/// <summary>
///
/// </summary>
/// <param name="gestiondecontrat"></param>
public GestionDeContratVueModele(GestionDeContrat gestiondecontrat)
{
this.gestiondecontrat = gestiondecontrat;
this.contrats = new ObservableCollection<ContratVueModele>();
foreach (contract contrat in this.gestiondecontrat.ListeDeContrat())
{
this.contrats.Add(new ContratVueModele() { NOMDUCONTRAT=contrat.title,DATEDEDEBUT=contrat.activeon.Value, DATEDEFIN=contrat.expireson.Value,LESTATUT=contrat.statecode.formattedvalue});
}
this.collectionView = CollectionViewSource.GetDefaultView(this.contrats);
if (this.collectionView == null)
throw new NullReferenceException("collectionView");
this.collectionView.CurrentChanged += new EventHandler(collectionView_CurrentChanged);
}
/// <summary>
///
/// </summary>
public ICommand ActiverContratCommand {
get
{
if (this.activerContratCommand == null)
this.activerContratCommand = new CommandeDeRelais(() => this.ActiverContrat(), () => this.CanActiverContrat());
return this.activerContratCommand;
}
}
/// <summary>
///
/// </summary>
public ICommand CopierContratCommand
{
get
{
if (this.copierContratCommand == null)
this.copierContratCommand = new CommandeDeRelais(() => this.CopierContrat(), () => this.CanCopierContrat());
return this.copierContratCommand;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
private bool CanCopierContrat()
{
return true;
//throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
private void CopierContrat()
{
this.gestiondecontrat.copier();
//throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
//public ICommand NextPageCommand {
// get {
// if(this.nextPageCommand==null)
// this.nextPageCommand = new CommandeDeRelais(() => this.NextPage(), () => this.CanNextPage());
// return nextPageCommand;
// }
//}
/// <summary>
///
/// </summary>
//private void NextPage() {
// this.pagingView.MoveToNextPage();
//}
/// <summary>
///
/// </summary>
/// <returns></returns>
//private bool CanNextPage() {
// return this.pagingView.PageCount > 1;//true; //Todo à reimplementer correctement
//}
/// <summary>
/// Permet de gérer le passage à la page précedente
/// </summary>
//private void PreviousPage()
//{
// this.pagingView.MoveToPreviousPage();
//}
/// <summary>
/// Vérifie si on peut passer à la page précédente
/// </summary>
/// <returns>un booléen qui nous dit si on peut passer à la commande précédente</returns>
//private bool CanPreviousPage()
//{
// return this.pagingView.MoveCurrentToPrevious();//true; //Todo à reimplementer correctement
//}
/// <summary>
/// Lecteur qui prend en charge la commande associée au bouton
/// </summary>
//public ICommand PreviousPageCommand
//{
// get
// {
// if (this.previousPageCommand==null)
// this.previousPageCommand = new CommandeDeRelais(() => this.PreviousPage(), () => this.CanPreviousPage());
// return previousPageCommand;
// }
//}
/// <summary>
/// Lecteur de la page courante
/// </summary>
public int CURRENTPAGE {
get {
return this.currentpage;
}
}
/// <summary>
/// Lecteur qui prend en charge la Commande qui s'exécute quand on appuie sur le bouton créer
/// ce bouton sera lié à cette propriété
/// </summary>
public ICommand CreerContratCommand {
get
{
if (this.creerContratCommand == null)
this.creerContratCommand = new CommandeDeRelais(() => this.CreerContrat(), () => this.CanCreerContrat());
return this.creerContratCommand;
}
}
/// <summary>
/// Lecteur sur la commande supprimer le contrat
/// </summary>
public ICommand SupprimerContratCommand {
get {
if (this.supprimerContratCommand == null)
this.supprimerContratCommand = new CommandeDeRelais(() => this.SupprimerContrat(), () => this.CanSupprimerContrat());
return this.supprimerContratCommand;
}
}
/// <summary>
///
/// </summary>
public ICommand ModifierContratCommand {
get {
if (this.modifierContratCommand == null)
this.modifierContratCommand = new CommandeDeRelais(() => this.ModifierContrat(), () => this.CanModifierContrat());
return this.modifierContratCommand;
}
}
/// <summary>
/// Propriété permettant de manipuler la
///Vue Modèle de la liste des contrats
/// </summary>
public ObservableCollection<ContratVueModele> Lescontrats
{
get
{
return this.contrats;
}
}
/// <summary>
/// Propriété permettant de manipuler la vue modèle
/// de selection du contrat
/// </summary>
public ContratVueModele SelectedContrat
{
get
{
return this.collectionView.CurrentItem as ContratVueModele;
}
}
/// <summary>
/// Propriété permettant manipuler la vue modèle qui vérifie si
/// que la vue est vide
/// </summary>
public bool SearchContainsNoMatch
{
get
{
return this.collectionView.IsEmpty;
}
}
/// <summary>
/// Propriété permettant manipuler la vue modèle
/// de recherche du contrat en fonction du nom ou du statut
/// </summary>
public string RechercheText
{
set
{
this.collectionView.Filter = (item) =>
{
if (item as ContratVueModele == null)
return false;
ContratVueModele contratvueModele = (ContratVueModele)item;
if (contratvueModele.NOMDUCONTRAT.Contains(value) || contratvueModele.LESTATUT.Contains(value))
return true;
return false;
};
this.OnPropertyChanged("SearchContainsNoMatch");
}
}
/// <summary>
/// Handler pour la sélection du contrat
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void collectionView_CurrentChanged(object sender, EventArgs e)
{
OnPropertyChanged("SelectedContrat");
//Dispose();
//throw new NotImplementedException();
}
/// <summary>
///
/// </summary>
private void CreerContrat() {
try
{
this.gestiondecontrat.creer();
foreach (contract contrat in this.gestiondecontrat.ListeDeContrat())
{
this.contrats.Add(new ContratVueModele() { NOMDUCONTRAT=contrat.title, DATEDEDEBUT=contrat.activeon.Value, DATEDEFIN=contrat.expireson.Value, ADRESSE=contrat.serviceaddress.name, NET=contrat.netprice.Value});
}
}
catch(Exception creerEx){
creerEx.Message.ToString();
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
private bool CanCreerContrat() {
return true;
}
/// <summary>
///
/// </summary>
private void ActiverContrat()
{
try
{
this.gestiondecontrat.ActiverContrat(this.SelectedContrat.CONTRAT);
}
catch (Exception activerEx)
{
activerEx.Message.ToString();
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
private bool CanActiverContrat()
{
return this.SelectedContrat.LESTATUT != "Annulé";
}
/// <summary>
///
/// </summary>
private void SupprimerContrat()
{
try
{
this.gestiondecontrat.supprimer();
this.contrats.Remove(new ContratVueModele(this.SelectedContrat.CONTRAT));
}
catch (Exception supprimerEx)
{
supprimerEx.Message.ToString();
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
private bool CanSupprimerContrat()
{
return this.contrats.Count>0;//true;
}
/// <summary>
///
/// </summary>
private void ModifierContrat() {
this.gestiondecontrat.modifier();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
private bool CanModifierContrat() {
return this.SelectedContrat != null;
}
/// <summary>
///
/// </summary>
private void OuvrirDocumentContrat()
{
try
{
// Contrat newContrat = this.gestiondecontrat.CreerContrat(new Contrat(new ModeleDeContrat()));
//this.contrats.Add(new ContratVueModele(newContrat));
}
catch (Exception OuvrirDocumentContratEx)
{
OuvrirDocumentContratEx.Message.ToString();
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
private bool CanOuvrirDocumentContrat()
{
return true;
}
}
}
code behind
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace GestionDeContrats_Offres_ClientsGUI.VueModele
{
/// <summary>
/// Logique d'interaction pour UserControlGestionContrat.xaml
/// </summary>
public partial class UserControlGestionContrat : UserControl
{
private readonly GestionDeContratVueModele vuemodelecontrat;
/// <summary>
///
/// </summary>
public UserControlGestionContrat()
{
InitializeComponent();
this.vuemodelecontrat = new GestionDeContratVueModele();
this.DataContext = this.vuemodelecontrat;
}
}
}
In my MainWindow.xaml i just integrate all usercontrol
Thanks
Best regards
i resolved a part of this problem by using listview, by making some changes on design. i made somes methods and called this in my constructor, now adding on list is making into these methods. I also change binding mode.
Now i have another problem.
When i run my solution on visual studio it takes a lot of time more then half an hour.it consumes many resources. and it is not finished correctly.
Related
How predicate filters works with ListCollectionView?
In my case i have ListCollectionView<Users> FilteredUserList.
To filter single value i am using
private void AddFilterAndRefresh(string name, Predicate<User> predicate)
{
//Adds filter to filter list
Filters.Add(name, predicate);
//Filters doesn't fire event automatically
OnPropertyChanged("Filters");
//Refresh list to by correctly displayed
FilteredUserList.Refresh();
}
Usage example AddFilterAndRefresh(key, user => user.Name.Contains(u.Name))
Now things from here is not understandable for me. When i use function above, in datagridview is shown only one row. So it means that before filter all values are "true" to show, but when i pass filter all values becomes false and one value true?
For stacking filters i use
private bool FilterEntries(object obj)
{
User c = (User)obj;
return Filters.Values.Aggregate(true, (prevValue, predicate) => prevValue && predicate(c));
}
But I want to make excel like filter, when user checks values what to show. It means that i have to filter multiple values. When i do foreach(User u in SelectedOptions) {AddFilterAndRefresh()} Datagrid is empty - but it is obvious because after one filter datagrid shows one row. So how to filter multiple values?
Well. I did some modifications. It works but not correctly. Imagine I have user list:
Tabelis Name Departament
5 Marius Some
20 Darius unknown
20 Koste unknown
20 Gediminas unknown
20 Nerijus tech
Now if i exclude by Departament "unknown" (uncheck chekbox) it filters ok :
Tabelis Name Departament
5 Marius Some
20 Nerijus tech
Now when i uncheck by Tabelis "20" it filter ok:
Tabelis Name Departament
5 Marius Some
But when i out check on Tabelis "20" again it filters wrong:
Tabelis Name Departament
5 Marius Some
20 Darius unknown
20 Koste unknown
20 Gediminas unknown
20 Nerijus tech
Turns Departament values on.
Where i am doing a mistake?
view:
<DataGrid x:Name="myGrd"
DataContext="{Binding ElementName=userPage, Path=DataContext}"
SelectionMode="Single"
SelectionUnit="Cell"
CurrentItem="{Binding SelectedUser, Mode=TwoWay}"
CurrentColumn="{Binding CurrentColumn, Mode=TwoWay}"
IsReadOnly="True"
CanUserResizeColumns="False"
Grid.Row="1"
ItemsSource="{Binding FilteredUserList}"
AutoGenerateColumns="True"
CanUserAddRows="False">
<DataGrid.Resources>
<!--Popup-->
<ContextMenu x:Key="ContextMenu">
<ContextMenu.Items>
<MenuItem Header="Filter by Selection" Command="{Binding IncludeCommand, Source={x:Reference vm}}"/>
<MenuItem Header="Filter exclude Selection" Command="{Binding ExcludeCommand, Source={x:Reference vm}}"/>
<MenuItem Header="Remove all Filters" Command="{Binding RemoveAllFiltersCommand, Source={x:Reference vm}}" Visibility="{Binding Filters.Count, Source={x:Reference vm}, Converter={Wpf:VisibilityConverter}}"/>
</ContextMenu.Items>
</ContextMenu>
<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader" x:Name="FilterHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Margin="0,0,0,10" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" HorizontalAlignment="Center"/>
<ToggleButton Name="FilterButton"
Content="[-F-]"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.GenerateExcelFilterViewItemsCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridColumnHeader}, Path=Content}"
>
</ToggleButton>
<Popup Name="MyPopup"
StaysOpen="False"
Placement="Right"
IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}"
PlacementTarget="{Binding ElementName=FilterButton}"
>
<Border CornerRadius="0" BorderThickness="2" BorderBrush="{StaticResource AQBlueBrush}">
<StackPanel Width="Auto" Background="{StaticResource AQBackgroundLightBrush}" MinWidth="100">
<TextBlock Margin="2" Text="Filter" Foreground="{StaticResource AQVeryDarkBlueBrush}"/>
<Separator/>
<ListView Padding="5"
MaxHeight="150"
MinHeight="80"
Background="Transparent"
BorderThickness="0"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.FilterList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.IsSelectedItemsCommand}"
CommandParameter="{Binding}"/>
<ContentPresenter Content="{Binding Value}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Margin="2,0,2,2"
Content="Submit"
Foreground="{StaticResource AQVeryDarkBlueBrush}"
Background="AliceBlue"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.AddMultipleFiltersAndRefreshCommand}"/>
</StackPanel>
</Border>
</Popup>
</StackPanel>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
</Style>
</DataGrid.CellStyle>
</DataGrid>
ViewModel:
public class UsersViewModel: BaseViewModel
{
#region Private Properties
/// <summary>
/// User's information holding list that could be used
/// </summary>
private ObservableCollection<User> UserList = new ObservableCollection<User>();
/// <summary>
/// Selected Header in datagrid
/// </summary>
private string SelectedColumnHeader { get; set; }
/// <summary>
/// List of isSelected Values
/// </summary>
private ObservableCollection<FilterPredicate> SelectedFilters { get; set; } = new ObservableCollection<FilterPredicate>();
#endregion
#region public Properties
/// <summary>
/// Filter list used to excel like filter functionality
/// </summary>
public ObservableCollection<FilterPredicate> FilterList { get; set; }
/// <summary>
/// Filters every filter used is saved in this dictionary
/// </summary>
public List<Predicate<User>> Filters { get; set; } = new List<Predicate<User>>();
/// <summary>
/// Filtered user list
/// </summary>
public ListCollectionView FilteredUserList { get; set; }
/// <summary>
/// Current Selected Object
/// </summary>
public User SelectedUser { get; set; }
/// <summary>
/// Current Selected Column
/// </summary>
public DataGridColumn CurrentColumn { get; set; }
public bool PopupVisible { get; set; }
public UIElement FilterButton { get; set; }
#endregion
#region Commands
public ICommand ExcludeCommand { get; set; }
public ICommand IncludeCommand { get; set; }
public ICommand RemoveAllFiltersCommand { get; set; }
public ICommand RemoveFilterCommand { get; set; }
public ICommand GenerateExcelFilterViewItemsCommand { get; set; }
public ICommand IsSelectedItemsCommand { get; set; }
public ICommand AddMultipleFiltersAndRefreshCommand { get; set; }
#endregion
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public UsersViewModel()
{
//Populate UserList
GetUsers();
//Set filtered user list with values
FilterList = new ObservableCollection<FilterPredicate>();
FilteredUserList = new ListCollectionView(UserList);
ExcludeCommand = new RelayParamCommand((e) => ExcludeFilter(SelectedUser));
IncludeCommand = new RelayParamCommand((e) => IncludeFilter(SelectedUser));
RemoveAllFiltersCommand = new RelayCommand(() => RemoveAllFiltersAndRefresh());
GenerateExcelFilterViewItemsCommand = new RelayParamCommand((e) => GenerateExcelFilterItems(e));
IsSelectedItemsCommand = new RelayParamCommand((e) => IsSelectedItems(e));
//AddMultipleFiltersAndRefreshCommand = new RelayCommand(AddMultipleFilterss);
FilteredUserList.Filter = (e) => { return FilterEntries(e); };
}
#endregion
#region Filter Methods
/// <summary>
/// Filter Collection view values
/// </summary>
private bool FilterEntries(object obj)
{
User c = (User)obj;
bool isIn = true;
if (Filters.Count == 0)
{
//return Filters.TrueForAll(x => x(c));
return isIn;
}
else
{
return Filters.TrueForAll(x => x(c));
// return Filters.Aggregate(true, (prevValue, predicate) => prevValue && predicate(c));
}
}
/// <summary>
/// Exclude selected value
/// </summary>
/// <param name="obj"></param>
public void ExcludeFilter(object obj)
{
User u = (User)obj;
switch (CurrentColumn.DisplayIndex)
{
case 0:
AddFilterAndRefresh(user => !user.Tabelis.ToString().Contains(u.Tabelis.ToString()));
return;
case 1:
AddFilterAndRefresh(user => !user.Name.Contains(u.Name));
return;
case 2:
AddFilterAndRefresh(user => !user.Departament.Contains(u.Departament));
return;
}
}
/// <summary>
/// Include selected filter value
/// </summary>
/// <param name="obj">Object</param>
private void IncludeFilter(object obj)
{
User u = (User)obj;
switch (CurrentColumn.DisplayIndex)
{
case 0:
AddFilterAndRefresh(user => user.Tabelis.ToString().Contains(u.Tabelis.ToString()));
return;
case 1:
AddFilterAndRefresh(user => user.Name.Contains(u.Name));
return;
case 2:
AddFilterAndRefresh(user => user.Departament.Contains(u.Departament));
return;
}
}
/// <summary>
/// Add filter to Filter list
/// </summary>
/// <param name="name">Key</param>
/// <param name="predicate">Filter (predicate) object</param>
private void AddFilterAndRefresh(Predicate<User> predicate)
{
//Adds filter to filter list
Filters.Add(predicate);
//Filters doesn't fire event automatically
OnPropertyChanged("Filters");
//Refresh list to by correctly displayed
FilteredUserList.Refresh();
}
/// <summary>
/// Remove all filters from filter list
/// </summary>
private void RemoveAllFiltersAndRefresh()
{
Filters.Clear();
FilterList.Clear();
SelectedFilters.Clear();
FilteredUserList.Refresh();
OnPropertyChanged("Filters");
}
/// <summary>
/// Gets property to get filter items by this property value
/// </summary>
/// <param name="obj">Object property</param>
private void GenerateExcelFilterItems(object obj)
{
//set header name as string
SelectedColumnHeader = (string)obj;
//Clear Filter list
FilterList.Clear();
foreach(FilterPredicate i in SelectedFilters)
{
//insert right not selected value
//because after filter excecution that value dissapears
if (!FilterList.Contains(i) && i.ColumnName == SelectedColumnHeader) { FilterList.Add(i); }
}
//Fill filter list with new values depend on selectedColumnHeader property
FillFilterValues(SelectedColumnHeader);
}
/// <summary>
/// Remove or add "IsSelected" values to SelectedFilters List
/// </summary>
/// <param name="obj"></param>
private void IsSelectedItems(object obj)
{
var SelectedItem = (FilterPredicate)obj;
if (SelectedItem.IsSelected)
{
Filters.Add(AddMultipleFilters);
//Refresh list to by correctly displayed
FilteredUserList.Refresh();
}
else
{
FilterList[FilterList.IndexOf(SelectedItem)].IsSelected = false;
Filters.Add(AddMultipleFilters);
//RemoveMultipleFilters(SelectedItem);
if(!SelectedFilters.Contains(SelectedItem))
SelectedFilters.Add(SelectedItem);
//Refresh list to by correctly displayed
FilteredUserList.Refresh();
}
}
private bool AddMultipleFilters(object obj)
{
User u = (User)obj;
return FilterList.Any(x => x.IsSelected && x.Value.Contains(u.GetType().GetProperty(SelectedColumnHeader).GetValue(u).ToString()));
}
#endregion
#region helpers
/// <summary>
/// Fill filter items list with values from FilteredUsersList
/// </summary>
/// <param name="Property"></param>
private void FillFilterValues(string Property)
{
foreach (User u in FilteredUserList)
{
User i = u;
var item = new FilterPredicate(Property, u.GetType().GetProperty(Property).GetValue(i).ToString(), true);
if (!FilterList.Any(x => x.ColumnName == item.ColumnName && x.Value == item.Value))
FilterList.Add(item);
}
}
/// <summary>
/// Fill userList with data from database
/// </summary>
private void GetUsers()
{
UserList.Add(new User
{
Name = "Marius",
Departament = "some",
Tabelis = 5
});
UserList.Add(
new User
{
Name = "Darius",
Departament = "unknown",
Tabelis = 20
});
UserList.Add(
new User
{
Name = "Koste",
Departament = "unknown",
Tabelis = 20
});
UserList.Add(
new User
{
Name = "Gediminas",
Departament = "unknown",
Tabelis = 20
});
UserList.Add(
new User
{
Name = "Nerijus",
Departament = "Tech",
Tabelis = 20
});
}
#endregion
}
}
Filter predicate should return true for items which needs to be displayed, and false for hidden items.
CollectionView needs only one predicate. To filter by selected names, keep them in a list and compare CollectionView item for possible match with any name, not with each name.
private bool FilterEntries(object obj)
{
User u = (User)obj;
// converting original condition to ANY instead of EACH
return Filters.Values.Aggregate(false, (prevValue, predicate) => prevValue || predicate(u));
// looks like Filters is a Dictionary, maybe simplify condition?
return Filters.ContainsKey(u.Name);
// or maybe search in a List without multiple predicates
return Filters.Keys.Any(name => name.Contains(u.Name));
}
Hi im working on an Wpf MVVM project, but i can't figure it out how to bind a button with the Command attribute in Xaml to a RelayCommand in the Viewmodel, i found multiple answer's online but i don't get it (implementing the ICommand interface and canexecute and stuff..) , the thing is that i have an already created project made by other developers where they simply , bind the button in Xaml like so :
heres the full code in the View :
<Window x:Class="MvvmLight3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mvvm="http://www.galasoft.ch/mvvmlight"
xmlns:ignore="http://www.galasoft.ch/ignore"
mc:Ignorable="d ignore"
Height="300"
Width="300"
Title="MVVM Light Application"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<TextBlock FontSize="36"
FontWeight="Bold"
Foreground="Purple"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" ><Run Text="WelcomeTitle"/><InlineUIContainer>
<Label Content="{Binding welcome}"/>
</InlineUIContainer></TextBlock>
<Button Content="Button" Command="{Binding The_Command}" HorizontalAlignment="Left" Margin="170,80,0,0" VerticalAlignment="Top" Width="75">
</Button>
</Grid>
and in the ViewModel the full code is :
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using MvvmLight3.Model;
using System.Windows.Input;
namespace MvvmLight3.ViewModel
{
/// <summary>
/// This class contains properties that the main View can data bind to.
/// <para>
/// See http://www.mvvmlight.net
/// </para>
/// </summary>
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
/// <summary>
/// The <see cref="WelcomeTitle" /> property's name.
/// </summary>
public const string WelcomeTitlePropertyName = "WelcomeTitle";
private string _welcomeTitle = string.Empty;
private string _welcome = "this work";
/// <summary>
/// Gets the WelcomeTitle property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string WelcomeTitle
{
get
{
return _welcomeTitle;
}
set
{
Set(ref _welcomeTitle, value);
}
}
public string welcome
{
get
{
return _welcome;
}
set
{
Set(ref _welcome, value);
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
_dataService.GetData(
(item, error) =>
{
if (error != null)
{
// Report error here
return;
}
WelcomeTitle = item.Title;
});
}
private RelayCommand _The_Command;
public RelayCommand The_Command
{
get
{
return _The_Command
?? (_The_Command = new RelayCommand(
() =>
{
//some Code
}));
}
}
////public override void Cleanup()
////{
//// // Clean up if needed
//// base.Cleanup();
////}
}
}
in my case the execution dont enter the RelayCommand.
the code in the ViewModelLocator.cs
/*
In App.xaml:
<Application.Resources>
<vm:ViewModelLocatorTemplate xmlns:vm="clr-namespace:MvvmLight3.ViewModel"
x:Key="Locator" />
</Application.Resources>
In the View:
DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}"
*/
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using MvvmLight3.Model;
namespace MvvmLight3.ViewModel
{
/// <summary>
/// This class contains static references to all the view models in the
/// application and provides an entry point for the bindings.
/// <para>
/// See http://www.mvvmlight.net
/// </para>
/// </summary>
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<MainViewModel>();
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
/// <summary>
/// Cleans up all the resources.
/// </summary>
public static void Cleanup()
{
}
}
}
the project is MVVMLight (wpf451) template .
Thank you.
It worked, it's something silly, actually if you notice my relay Command (The_Command) was empty and although it had a comment inside it (//some Code), a breakpoint on the get or return never went through when I click the button.
Solution:
after I added a functioning code inside the Command it worked: added for example a simple MessageBox.Show. so the Binding was correct. For the info I'm using the VisualStudio Enterprise 2017 .
Hi I have integrated usercontrol(Loader) on my xaml page.
I want to show this loader on the fronpage.
if some another pages is loading.
<control:LoadingAnimation x:Name="ldrControl" Margin="100,100,100,150" Visibility="{Binding IsLoaderVisibile}" />
I am able to show this control but when another pages are loading then It's hided.
I have to show this control in front of the pages.
this will work ,
xmlns:local="clr-namespace:WpfApplication156"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Width="{Binding RelativeSource={RelativeSource Self},
Path=Height,
Mode=TwoWay}"
Height="120"
Background="Transparent"
IsVisibleChanged="HandleVisibleChanged"
Opacity="0"
Visibility="Hidden">
<Viewbox HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Canvas Width="120"
Height="120"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Loaded="HandleLoaded"
RenderTransformOrigin="0.5,0.5"
Unloaded="HandleUnloaded">
<Canvas.Resources>
<Style TargetType="Ellipse">
<Setter Property="Width" Value="20" />
<Setter Property="Height" Value="20" />
<Setter Property="Stretch" Value="Fill" />
<Setter Property="Fill">
<Setter.Value>
<Binding Path="Foreground">
<Binding.RelativeSource>
<RelativeSource AncestorType="{x:Type local:ProgressBar}" Mode="FindAncestor" />
</Binding.RelativeSource>
</Binding>
</Setter.Value>
</Setter>
</Style>
</Canvas.Resources>
<Ellipse x:Name="C0" Opacity="1.0" />
<Ellipse x:Name="C1" Opacity="0.9" />
<Ellipse x:Name="C2" Opacity="0.8" />
<Ellipse x:Name="C3" Opacity="0.7" />
<Ellipse x:Name="C4" Opacity="0.6" />
<Ellipse x:Name="C5" Opacity="0.5" />
<Ellipse x:Name="C6" Opacity="0.4" />
<Ellipse x:Name="C7" Opacity="0.3" />
<Ellipse x:Name="C8" Opacity="0.2" />
<Canvas.RenderTransform>
<RotateTransform x:Name="SpinnerRotate" Angle="0" />
</Canvas.RenderTransform>
</Canvas>
</Viewbox>
</UserControl>
public partial class ProgressBar
{
#region Public Fields
/// <summary>
/// Spinning Speed. Default is 60, that's one rotation per second.
/// </summary>
public static readonly DependencyProperty RotationsPerMinuteProperty =
DependencyProperty.Register(
"RotationsPerMinute",
typeof(double),
typeof(ProgressBar),
new PropertyMetadata(60.0));
/// <summary>
/// Startup time in milliseconds, default is a second.
/// </summary>
public static readonly DependencyProperty StartupDelayProperty =
DependencyProperty.Register(
"StartupDelay",
typeof(int),
typeof(ProgressBar),
new PropertyMetadata(1000));
#endregion Public Fields
#region Private Fields
/// <summary>
/// Timer for the Animation.
/// </summary>
private readonly DispatcherTimer animationTimer;
/// <summary>
/// Mouse Cursor.
/// </summary>
private Cursor originalCursor;
#endregion Private Fields
#region Public Constructors
/// <summary>
/// Initializes a new instance of the ProgressBar class.
/// </summary>
public ProgressBar()
{
InitializeComponent();
this.animationTimer = new DispatcherTimer(DispatcherPriority.Normal, Dispatcher);
}
#endregion Public Constructors
#region Public Properties
/// <summary>
/// Gets or sets the spinning speed. Default is 60, that's one rotation per second.
/// </summary>
public double RotationsPerMinute
{
get
{
return (double)this.GetValue(RotationsPerMinuteProperty);
}
set
{
this.SetValue(RotationsPerMinuteProperty, value);
}
}
/// <summary>
/// Gets or sets the startup time in milliseconds, default is a second.
/// </summary>
public int StartupDelay
{
get
{
return (int)this.GetValue(StartupDelayProperty);
}
set
{
this.SetValue(StartupDelayProperty, value);
}
}
#endregion Public Properties
#region Private Methods
/// <summary>
/// Apply a single rotation transformation.
/// </summary>
/// <param name="sender">Sender of the Event: the Animation Timer.</param>
/// <param name="e">Event arguments.</param>
private void HandleAnimationTick(object sender, EventArgs e)
{
this.SpinnerRotate.Angle = (this.SpinnerRotate.Angle + 36) % 360;
}
/// <summary>
/// Control was loaded: distribute circles.
/// </summary>
/// <param name="sender">Sender of the Event: I wish I knew.</param>
/// <param name="e">Event arguments.</param>
private void HandleLoaded(object sender, RoutedEventArgs e)
{
this.SetPosition(C0, 0.0);
this.SetPosition(C1, 1.0);
this.SetPosition(C2, 2.0);
this.SetPosition(C3, 3.0);
this.SetPosition(C4, 4.0);
this.SetPosition(C5, 5.0);
this.SetPosition(C6, 6.0);
this.SetPosition(C7, 7.0);
this.SetPosition(C8, 8.0);
}
/// <summary>
/// Control was unloaded: stop spinning.
/// </summary>
/// <param name="sender">Sender of the event.</param>
/// <param name="e">Event arguments.</param>
private void HandleUnloaded(object sender, RoutedEventArgs e)
{
this.StopSpinning();
}
/// <summary>
/// Visibility property was changed: start or stop spinning.
/// </summary>
/// <param name="sender">Sender of the event.</param>
/// <param name="e">Event arguments.</param>
private void HandleVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Don't give the developer a headache.
////if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
////{
//// return;
////}
bool isVisible = (bool)e.NewValue;
if (isVisible)
{
this.StartDelay();
}
else
{
this.StopSpinning();
}
}
/// <summary>
/// Calculate position of a circle.
/// </summary>
/// <param name="ellipse">The circle.</param>
/// <param name="sequence">Sequence number of the circle.</param>
private void SetPosition(Ellipse ellipse, double sequence)
{
ellipse.SetValue(
Canvas.LeftProperty,
50.0 + (Math.Sin(Math.PI * ((0.2 * sequence) + 1)) * 50.0));
ellipse.SetValue(
Canvas.TopProperty,
50 + (Math.Cos(Math.PI * ((0.2 * sequence) + 1)) * 50.0));
}
/// <summary>
/// Startup Delay.
/// </summary>
private void StartDelay()
{
this.originalCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = Cursors.Wait;
// Startup
this.animationTimer.Interval = new TimeSpan(0, 0, 0, 0, this.StartupDelay);
this.animationTimer.Tick += this.StartSpinning;
this.animationTimer.Start();
}
/// <summary>
/// Start Spinning.
/// </summary>
/// <param name="sender">Sender of the event.</param>
/// <param name="e">Event Arguments.</param>
private void StartSpinning(object sender, EventArgs e)
{
this.animationTimer.Stop();
this.animationTimer.Tick -= this.StartSpinning;
// 60 secs per minute, 1000 millisecs per sec, 10 rotations per full circle:
this.animationTimer.Interval = new TimeSpan(0, 0, 0, 0, (int)(6000 / this.RotationsPerMinute));
this.animationTimer.Tick += this.HandleAnimationTick;
this.animationTimer.Start();
this.Opacity = 1;
Mouse.OverrideCursor = this.originalCursor;
}
/// <summary>
/// The control became invisible: stop spinning (animation consumes CPU).
/// </summary>
private void StopSpinning()
{
this.animationTimer.Stop();
this.animationTimer.Tick -= this.HandleAnimationTick;
this.Opacity = 0;
}
#endregion Private Methods
}
//********************************************
IsLoaded=true; set in Relay Command
IsLoaded =false;
private Visibility isLoaded;
public Visibility IsLoaded
{
get
{
return this.isLoaded;
}
set
{
this.isLoaded = value;
RasisePropertyChange("IsLoaded");
}
}
<vm:ProgressBar x:Name="ProgressBar"
Grid.Row="2"
Grid.Column="1"
Width="140"
Margin="12"
Padding="10"
Visibility="{Binding IsLoaded}">
<vm:ProgressBar.Foreground>
<RadialGradientBrush Center="0.5,0.5" GradientOrigin="0.4,0.4" RadiusX="0.5" RadiusY="0.5">
<RadialGradientBrush.GradientStops>
<GradientStop Offset="0" Color="Transparent" />
<GradientStop Offset="1" Color="DimGray" />
</RadialGradientBrush.GradientStops>
</RadialGradientBrush>
</vm:ProgressBar.Foreground>
</vm:ProgressBar>
I am writing a wpf application and implements mvvm light tool. The GUI looks like:
Every time when a user click on button, it should change the content on the right side, marked with red border. The XAML code:
<igWpf:XamRibbonWindow x:Class="BackupCustomizing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ignore="http://www.ignore.com"
xmlns:ig="http://schemas.infragistics.com/xaml"
xmlns:views="clr-namespace:BackupCustomizing.Views"
xmlns:igWpf="http://schemas.infragistics.com/xaml/wpf"
mc:Ignorable="d ignore"
Height="400"
Width="700"
Title="Backup customizing V0.1"
DataContext="{Binding Main, Source={StaticResource Locator}}" ResizeMode="NoResize">
<igWpf:XamRibbonWindow.Resources>
<DataTemplate DataType="{x:Type views:ServerView}"></DataTemplate>
</igWpf:XamRibbonWindow.Resources>
<ig:ThemeManager.Theme>
<ig:Office2013Theme />
</ig:ThemeManager.Theme>
<igWpf:RibbonWindowContentHost x:Name="_content"
Theme="Office2013"
igWpf:RibbonWindowContentHost.ApplicationAccentColor="#0072C6">
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<views:NavigationView Grid.Column="0"/>
<ContentPresenter Content="{Binding}" Grid.Column="1"/>
</Grid>
</igWpf:RibbonWindowContentHost>
</igWpf:XamRibbonWindow>
and the code behind:
using System.Windows;
using BackupCustomizing.ViewModel;
using Infragistics.Themes;
using Infragistics.Windows.Ribbon;
namespace BackupCustomizing
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : XamRibbonWindow
{
/// <summary>
/// Initializes a new instance of the MainWindow class.
/// </summary>
public MainWindow()
{
InitializeComponent();
Closing += (s, e) => ViewModelLocator.Cleanup();
}
}
}
As you can see the code above, I tried with:
<igWpf:XamRibbonWindow.Resources>
<DataTemplate DataType="{x:Type views:ServerView}"></DataTemplate>
</igWpf:XamRibbonWindow.Resources>
and the content presenter:
<ContentPresenter Content="{Binding}" Grid.Column="1"/>
and here I stocked, how to continue?
The ViewModel code:
using BackupCustomizing.Model;
using GalaSoft.MvvmLight;
namespace BackupCustomizing.ViewModel
{
/// <summary>
/// This class contains properties that the main View can data bind to.
/// <para>
/// See http://www.galasoft.ch/mvvm
/// </para>
/// </summary>
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
private string _welcomeTitle = string.Empty;
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
_dataService.GetData(
(item, error) =>
{
});
}
}
}
To get you code working in minimum changes
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
private string _welcomeTitle = string.Empty;
private ViewModelBase detailsViewModel = null;
public ViewModelBase DetailsViewModel{
get { return detailsViewModel;}
set { detailsViewModel = value; RaisePropertyChanged("DetailsViewModel"); }
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
_dataService.GetData(
(item, error) =>
{
detailsViewModel = new ServerViewModel(item); //ViewModel for the ServerView
});
}
}
<igWpf:XamRibbonWindow.Resources>
<DataTemplate DataType="{x:Type viewModel:ServerViewModel}">
<views:ServerView />
</DataTemplate>
</igWpf:XamRibbonWindow.Resources>
<ContentPresenter Content="{Binding DetailsViewModel}" Grid.Column="1"/>
There are other techniques to do MVVM, I am just showing the way to do it with the approach you have started with. Problem with this approach is that it will not scale well to large number of views in the ContentPresenter.
I'm pretty much new to the MVVM architecture design...
I was struggling lately to find a suitable control already written for such a purpose but had no luck, so I reused parts of XAML from another similar control and got make my own.
What I want to achieve is:
Have a reusable View (usercontrol) + viewmodel (to bind to) to be able to use inside other views as a modal overlay showing a dialog that disables the rest of the view, and shows a dialog over the it.
How I wanted to implement it:
create a viewmodel that takes string(message) and action+string collection(buttons)
viewmodel creates a collection of ICommands that call those actions
dialog view binds to the its viewmodel that will be exposed as property of another viewmodel (parent)
dialog view is put into the xaml of the parent like this:
pseudoXAML:
<usercontrol /customerview/ ...>
<grid>
<grid x:Name="content">
<various form content />
</grid>
<ctrl:Dialog DataContext="{Binding DialogModel}" Message="{Binding Message}" Commands="{Binding Commands}" IsShown="{Binding IsShown}" BlockedUI="{Binding ElementName=content}" />
</grid>
</usercontrol>
So here the modal dialog gets the datacontext from the DialogModel property of the Customer viewmodel, and binds commands and message. It would be also bound to some other element (here 'content') that needs to be disabled when the dialog shows (binding to IsShown). When you click some button in the dialog the associated command is called that simply calls the associated action that was passed in the constructor of the viewmodel.
This way I would be able to call Show() and Hide() of the dialog on the dialog viewmodel from inside the Customer viewmodel and alter the dialog viewmodel as needed.
It would give me only one dialog at a time but that is fine.
I also think that the dialog viewmodel would remain unittestable, since the unittests would cover the calling of the commands that ought to be created after it being created with Actions in the constructor. There would be a few lines of codebehind for the dialog view, but very little and pretty dumb (setters getters, with almost no code).
What concerns me is:
Is this ok?
Are there any problems I could get into?
Does this break some MVVM principles?
Thanks a lot!
EDIT: I posted my complete solution so you can have a better look. Any architectural comments welcome. If you see some syntax that can be corrected the post is flagged as community wiki.
Well not exactly an answer to my question, but here is the result of doing this dialog, complete with code so you can use it if you wish - free as in free speech and beer:
XAML Usage in another view (here CustomerView):
<UserControl
x:Class="DemoApp.View.CustomerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:DemoApp.View"
>
<Grid>
<Grid Margin="4" x:Name="ModalDialogParent">
<put all view content here/>
</Grid>
<controls:ModalDialog DataContext="{Binding Dialog}" OverlayOn="{Binding ElementName=ModalDialogParent, Mode=OneWay}" IsShown="{Binding Path=DialogShown}"/>
</Grid>
</UserControl>
Triggering from parent ViewModel (here CustomerViewModel):
public ModalDialogViewModel Dialog // dialog view binds to this
{
get
{
return _dialog;
}
set
{
_dialog = value;
base.OnPropertyChanged("Dialog");
}
}
public void AskSave()
{
Action OkCallback = () =>
{
if (Dialog != null) Dialog.Hide();
Save();
};
if (Email.Length < 10)
{
Dialog = new ModalDialogViewModel("This email seems a bit too short, are you sure you want to continue saving?",
ModalDialogViewModel.DialogButtons.Ok,
ModalDialogViewModel.CreateCommands(new Action[] { OkCallback }));
Dialog.Show();
return;
}
if (LastName.Length < 2)
{
Dialog = new ModalDialogViewModel("The Lastname seems short. Are you sure that you want to save this Customer?",
ModalDialogViewModel.CreateButtons(ModalDialogViewModel.DialogMode.TwoButton,
new string[] {"Of Course!", "NoWay!"},
OkCallback,
() => Dialog.Hide()));
Dialog.Show();
return;
}
Save(); // if we got here we can save directly
}
Here is the code:
ModalDialogView XAML:
<UserControl x:Class="DemoApp.View.ModalDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root">
<UserControl.Resources>
<ResourceDictionary Source="../MainWindowResources.xaml" />
</UserControl.Resources>
<Grid>
<Border Background="#90000000" Visibility="{Binding Visibility}">
<Border BorderBrush="Black" BorderThickness="1" Background="AliceBlue"
CornerRadius="10,0,10,0" VerticalAlignment="Center"
HorizontalAlignment="Center">
<Border.BitmapEffect>
<DropShadowBitmapEffect Color="Black" Opacity="0.5" Direction="270" ShadowDepth="0.7" />
</Border.BitmapEffect>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource ModalDialogHeader}" Text="{Binding DialogHeader}" Grid.Row="0"/>
<TextBlock Text="{Binding DialogMessage}" Grid.Row="1" TextWrapping="Wrap" Margin="5" />
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Grid.Row="2">
<ContentControl HorizontalAlignment="Stretch"
DataContext="{Binding Commands}"
Content="{Binding}"
ContentTemplate="{StaticResource ButtonCommandsTemplate}"
/>
</StackPanel>
</Grid>
</Border>
</Border>
</Grid>
</UserControl>
ModalDialogView code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace DemoApp.View
{
/// <summary>
/// Interaction logic for ModalDialog.xaml
/// </summary>
public partial class ModalDialog : UserControl
{
public ModalDialog()
{
InitializeComponent();
Visibility = Visibility.Hidden;
}
private bool _parentWasEnabled = true;
public bool IsShown
{
get { return (bool)GetValue(IsShownProperty); }
set { SetValue(IsShownProperty, value); }
}
// Using a DependencyProperty as the backing store for IsShown. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsShownProperty =
DependencyProperty.Register("IsShown", typeof(bool), typeof(ModalDialog), new UIPropertyMetadata(false, IsShownChangedCallback));
public static void IsShownChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
ModalDialog dlg = (ModalDialog)d;
dlg.Show();
}
else
{
ModalDialog dlg = (ModalDialog)d;
dlg.Hide();
}
}
#region OverlayOn
public UIElement OverlayOn
{
get { return (UIElement)GetValue(OverlayOnProperty); }
set { SetValue(OverlayOnProperty, value); }
}
// Using a DependencyProperty as the backing store for Parent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OverlayOnProperty =
DependencyProperty.Register("OverlayOn", typeof(UIElement), typeof(ModalDialog), new UIPropertyMetadata(null));
#endregion
public void Show()
{
// Force recalculate binding since Show can be called before binding are calculated
BindingExpression expressionOverlayParent = this.GetBindingExpression(OverlayOnProperty);
if (expressionOverlayParent != null)
{
expressionOverlayParent.UpdateTarget();
}
if (OverlayOn == null)
{
throw new InvalidOperationException("Required properties are not bound to the model.");
}
Visibility = System.Windows.Visibility.Visible;
_parentWasEnabled = OverlayOn.IsEnabled;
OverlayOn.IsEnabled = false;
}
private void Hide()
{
Visibility = Visibility.Hidden;
OverlayOn.IsEnabled = _parentWasEnabled;
}
}
}
ModalDialogViewModel:
using System;
using System.Windows.Input;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Windows;
using System.Linq;
namespace DemoApp.ViewModel
{
/// <summary>
/// Represents an actionable item displayed by a View (DialogView).
/// </summary>
public class ModalDialogViewModel : ViewModelBase
{
#region Nested types
/// <summary>
/// Nested enum symbolizing the types of default buttons used in the dialog -> you can localize those with Localize(DialogMode, string[])
/// </summary>
public enum DialogMode
{
/// <summary>
/// Single button in the View (default: OK)
/// </summary>
OneButton = 1,
/// <summary>
/// Two buttons in the View (default: YesNo)
/// </summary>
TwoButton,
/// <summary>
/// Three buttons in the View (default: AbortRetryIgnore)
/// </summary>
TreeButton,
/// <summary>
/// Four buttons in the View (no default translations, use Translate)
/// </summary>
FourButton,
/// <summary>
/// Five buttons in the View (no default translations, use Translate)
/// </summary>
FiveButton
}
/// <summary>
/// Provides some default button combinations
/// </summary>
public enum DialogButtons
{
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration Ok
/// </summary>
Ok,
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration OkCancel
/// </summary>
OkCancel,
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration YesNo
/// </summary>
YesNo,
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration YesNoCancel
/// </summary>
YesNoCancel,
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration AbortRetryIgnore
/// </summary>
AbortRetryIgnore,
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration RetryCancel
/// </summary>
RetryCancel
}
#endregion
#region Members
private static Dictionary<DialogMode, string[]> _translations = null;
private bool _dialogShown;
private ReadOnlyCollection<CommandViewModel> _commands;
private string _dialogMessage;
private string _dialogHeader;
#endregion
#region Class static methods and constructor
/// <summary>
/// Creates a dictionary symbolizing buttons for given dialog mode and buttons names with actions to berform on each
/// </summary>
/// <param name="mode">Mode that tells how many buttons are in the dialog</param>
/// <param name="names">Names of buttons in sequential order</param>
/// <param name="callbacks">Callbacks for given buttons</param>
/// <returns></returns>
public static Dictionary<string, Action> CreateButtons(DialogMode mode, string[] names, params Action[] callbacks)
{
int modeNumButtons = (int)mode;
if (names.Length != modeNumButtons)
throw new ArgumentException("The selected mode needs a different number of button names", "names");
if (callbacks.Length != modeNumButtons)
throw new ArgumentException("The selected mode needs a different number of callbacks", "callbacks");
Dictionary<string, Action> buttons = new Dictionary<string, Action>();
for (int i = 0; i < names.Length; i++)
{
buttons.Add(names[i], callbacks[i]);
}
return buttons;
}
/// <summary>
/// Static contructor for all DialogViewModels, runs once
/// </summary>
static ModalDialogViewModel()
{
InitTranslations();
}
/// <summary>
/// Fills the default translations for all modes that we support (use only from static constructor (not thread safe per se))
/// </summary>
private static void InitTranslations()
{
_translations = new Dictionary<DialogMode, string[]>();
foreach (DialogMode mode in Enum.GetValues(typeof(DialogMode)))
{
_translations.Add(mode, GetDefaultTranslations(mode));
}
}
/// <summary>
/// Creates Commands for given enumeration of Actions
/// </summary>
/// <param name="actions">Actions to create commands from</param>
/// <returns>Array of commands for given actions</returns>
public static ICommand[] CreateCommands(IEnumerable<Action> actions)
{
List<ICommand> commands = new List<ICommand>();
Action[] actionArray = actions.ToArray();
foreach (var action in actionArray)
{
//RelayExecuteWrapper rxw = new RelayExecuteWrapper(action);
Action act = action;
commands.Add(new RelayCommand(x => act()));
}
return commands.ToArray();
}
/// <summary>
/// Creates string for some predefined buttons (English)
/// </summary>
/// <param name="buttons">DialogButtons enumeration value</param>
/// <returns>String array for desired buttons</returns>
public static string[] GetButtonDefaultStrings(DialogButtons buttons)
{
switch (buttons)
{
case DialogButtons.Ok:
return new string[] { "Ok" };
case DialogButtons.OkCancel:
return new string[] { "Ok", "Cancel" };
case DialogButtons.YesNo:
return new string[] { "Yes", "No" };
case DialogButtons.YesNoCancel:
return new string[] { "Yes", "No", "Cancel" };
case DialogButtons.RetryCancel:
return new string[] { "Retry", "Cancel" };
case DialogButtons.AbortRetryIgnore:
return new string[] { "Abort", "Retry", "Ignore" };
default:
throw new InvalidOperationException("There are no default string translations for this button configuration.");
}
}
private static string[] GetDefaultTranslations(DialogMode mode)
{
string[] translated = null;
switch (mode)
{
case DialogMode.OneButton:
translated = GetButtonDefaultStrings(DialogButtons.Ok);
break;
case DialogMode.TwoButton:
translated = GetButtonDefaultStrings(DialogButtons.YesNo);
break;
case DialogMode.TreeButton:
translated = GetButtonDefaultStrings(DialogButtons.YesNoCancel);
break;
default:
translated = null; // you should use Translate() for this combination (ie. there is no default for four or more buttons)
break;
}
return translated;
}
/// <summary>
/// Translates all the Dialogs with specified mode
/// </summary>
/// <param name="mode">Dialog mode/type</param>
/// <param name="translations">Array of translations matching the buttons in the mode</param>
public static void Translate(DialogMode mode, string[] translations)
{
lock (_translations)
{
if (translations.Length != (int)mode)
throw new ArgumentException("Wrong number of translations for selected mode");
if (_translations.ContainsKey(mode))
{
_translations.Remove(mode);
}
_translations.Add(mode, translations);
}
}
#endregion
#region Constructors and initialization
public ModalDialogViewModel(string message, DialogMode mode, params ICommand[] commands)
{
Init(message, Application.Current.MainWindow.GetType().Assembly.GetName().Name, _translations[mode], commands);
}
public ModalDialogViewModel(string message, DialogMode mode, params Action[] callbacks)
{
Init(message, Application.Current.MainWindow.GetType().Assembly.GetName().Name, _translations[mode], CreateCommands(callbacks));
}
public ModalDialogViewModel(string message, Dictionary<string, Action> buttons)
{
Init(message, Application.Current.MainWindow.GetType().Assembly.GetName().Name, buttons.Keys.ToArray(), CreateCommands(buttons.Values.ToArray()));
}
public ModalDialogViewModel(string message, string header, Dictionary<string, Action> buttons)
{
if (buttons == null)
throw new ArgumentNullException("buttons");
ICommand[] commands = CreateCommands(buttons.Values.ToArray<Action>());
Init(message, header, buttons.Keys.ToArray<string>(), commands);
}
public ModalDialogViewModel(string message, DialogButtons buttons, params ICommand[] commands)
{
Init(message, Application.Current.MainWindow.GetType().Assembly.GetName().Name, ModalDialogViewModel.GetButtonDefaultStrings(buttons), commands);
}
public ModalDialogViewModel(string message, string header, DialogButtons buttons, params ICommand[] commands)
{
Init(message, header, ModalDialogViewModel.GetButtonDefaultStrings(buttons), commands);
}
public ModalDialogViewModel(string message, string header, string[] buttons, params ICommand[] commands)
{
Init(message, header, buttons, commands);
}
private void Init(string message, string header, string[] buttons, ICommand[] commands)
{
if (message == null)
throw new ArgumentNullException("message");
if (buttons.Length != commands.Length)
throw new ArgumentException("Same number of buttons and commands expected");
base.DisplayName = "ModalDialog";
this.DialogMessage = message;
this.DialogHeader = header;
List<CommandViewModel> commandModels = new List<CommandViewModel>();
// create commands viewmodel for buttons in the view
for (int i = 0; i < buttons.Length; i++)
{
commandModels.Add(new CommandViewModel(buttons[i], commands[i]));
}
this.Commands = new ReadOnlyCollection<CommandViewModel>(commandModels);
}
#endregion
#region Properties
/// <summary>
/// Checks if the dialog is visible, use Show() Hide() methods to set this
/// </summary>
public bool DialogShown
{
get
{
return _dialogShown;
}
private set
{
_dialogShown = value;
base.OnPropertyChanged("DialogShown");
}
}
/// <summary>
/// The message shown in the dialog
/// </summary>
public string DialogMessage
{
get
{
return _dialogMessage;
}
private set
{
_dialogMessage = value;
base.OnPropertyChanged("DialogMessage");
}
}
/// <summary>
/// The header (title) of the dialog
/// </summary>
public string DialogHeader
{
get
{
return _dialogHeader;
}
private set
{
_dialogHeader = value;
base.OnPropertyChanged("DialogHeader");
}
}
/// <summary>
/// Commands this dialog calls (the models that it binds to)
/// </summary>
public ReadOnlyCollection<CommandViewModel> Commands
{
get
{
return _commands;
}
private set
{
_commands = value;
base.OnPropertyChanged("Commands");
}
}
#endregion
#region Methods
public void Show()
{
this.DialogShown = true;
}
public void Hide()
{
this._dialogMessage = String.Empty;
this.DialogShown = false;
}
#endregion
}
}
ViewModelBase has :
public virtual string DisplayName { get; protected set; }
and implements INotifyPropertyChanged
Some resources to put in the resource dictionary:
<!--
This style gives look to the dialog head (used in the modal dialog)
-->
<Style x:Key="ModalDialogHeader" TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="{StaticResource Brush_HeaderBackground}" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Padding" Value="4" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Margin" Value="5" />
<Setter Property="TextWrapping" Value="NoWrap" />
</Style>
<!--
This template explains how to render the list of commands as buttons (used in the modal dialog)
-->
<DataTemplate x:Key="ButtonCommandsTemplate">
<ItemsControl IsTabStop="False" ItemsSource="{Binding}" Margin="6,2">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button MinWidth="75" Command="{Binding Path=Command}" Margin="4" HorizontalAlignment="Right">
<TextBlock Text="{Binding Path=DisplayName}" Margin="2"></TextBlock>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
I have a custom open source FrameworkElement on my GitHub page that allows you to display modal content over the primary content.
The control can be used like this:
<c:ModalContentPresenter IsModal="{Binding DialogIsVisible}">
<TabControl Margin="5">
<Button Margin="55"
Padding="10"
Command="{Binding ShowModalContentCommand}">
This is the primary Content
</Button>
</TabItem>
</TabControl>
<c:ModalContentPresenter.ModalContent>
<Button Margin="75"
Padding="50"
Command="{Binding HideModalContentCommand}">
This is the modal content
</Button>
</c:ModalContentPresenter.ModalContent>
</c:ModalContentPresenter>
Features:
Displays arbitrary content.
Does not disable the primary content whilst the modal content is being displayed.
Disables mouse and keyboard access to the primary content whilst the modal content is displayed.
Is only modal to the content it is covering, not the entire application.
can be used in an MVVM friendly way by binding to the IsModal property.
I would approach this as a service that gets injected into your ViewModel, along the lines of the sample code below. To the extent what you want to do is in fact message box behavior, I would have my service implementation use a MessageBox!
I am using KISS here in order to present the concept. No code behind, and completely unit testable as shown.
As an aside, that Josh Smith example you are working off of was incredibly helpful to me also, even if it doesn't cover everything
HTH,
Berry
/// <summary>
/// Simple interface for visually confirming a question to the user
/// </summary>
public interface IConfirmer
{
bool Confirm(string message, string caption);
}
public class WPFMessageBoxConfirmer : IConfirmer
{
#region Implementation of IConfirmer
public bool Confirm(string message, string caption) {
return MessageBox.Show(message, caption, MessageBoxButton.YesNo) == MessageBoxResult.Yes;
}
#endregion
}
// SomeViewModel uses an IConfirmer
public class SomeViewModel
{
public ShellViewModel(ISomeRepository repository, IConfirmer confirmer)
{
if (confirmer == null) throw new ArgumentNullException("confirmer");
_confirmer = confirmer;
...
}
...
private void _delete()
{
var someVm = _masterVm.SelectedItem;
Check.RequireNotNull(someVm);
if (detailVm.Model.IsPersistent()) {
var msg = string.Format(GlobalCommandStrings.ConfirmDeletion, someVm.DisplayName);
if(_confirmer.Confirm(msg, GlobalCommandStrings.ConfirmDeletionCaption)) {
_doDelete(someVm);
}
}
else {
_doDelete(someVm);
}
}
...
}
// usage in the Production code
var vm = new SomeViewModel(new WPFMessageBoxConfirmer());
// usage in a unit test
[Test]
public void DeleteCommand_OnExecute_IfUserConfirmsDeletion_RemovesSelectedItemFrom_Workspaces() {
var confirmerMock = MockRepository.GenerateStub<IConfirmer>();
confirmerMock.Stub(x => x.Confirm(Arg<string>.Is.Anything, Arg<string>.Is.Anything)).Return(true);
var vm = new ShellViewModel(_repository, _crudConverter, _masterVm, confirmerMock, _validator);
vm.EditCommand.Execute(null);
Assert.That(vm.Workspaces, Has.Member(_masterVm.SelectedItem));
Assert.That(vm.Workspaces, Is.Not.Empty);
vm.DeleteCommand.Execute(null);
Assert.That(vm.Workspaces, Has.No.Member(_masterVm.SelectedItem));
Assert.That(vm.Workspaces, Is.Empty);
}