So I'm trying to have a splash screen that has a progress bar that increments as my application loads. Obviously I've had to edit code to remove any IP, thankfully most of this is simple enough that none of it really matters. But any help would be greatly appreciated.
Failed Solution 1
Here is the code for my application block:
public class App : Application
{
public void Run()
{
bool isLoaded = false;
ProgressSplashScreen splashScreen = new ProgressSplashScreen(() => { isLoaded = true; });
splashScreen.Show();
SpinWait.SpinUntil(() => isLoaded);
_bootStrapper = new BootStrapper(splashScreen);
_bootStrapper.Run();
splashScreen.Close();
}
}
Here is the code for my splash screen XAML:
<Window x:Class="Application.ProgressSplashScreen"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Splash" BorderBrush="Transparent"
AllowsTransparency="True"
Icon="../../Resources/Icons/Icon.ico"
WindowStartupLocation="CenterScreen"
WindowStyle="None" Width="640" Height="520"
Background="Transparent" Name="SplashWindow" Topmost="True" >
<Grid>
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<Image Source="../../Resources/Images/splash.jpg" Height="480" Width="640"/>
<Grid>
<ProgressBar Name="SplashProgress" Height="20" Minimum="0" Maximum="100"
BorderBrush="Transparent" />
<Label Name="SplashMessage" VerticalAlignment="Bottom"
HorizontalAlignment="Center"/>
</Grid>
</StackPanel>
</Grid>
</Window>
And the Code Behind for the Splash Screen XAML:
using System;
using System.Threading;
using System.Windows;
namespace Application
{
public partial class ProgressSplashScreen : Window
{
private SynchronizationContext _synchContext;
public ProgressSplashScreen(Action isLoaded)
{
this.Loaded += (sender, args) => isLoaded();
InitializeComponent();
_synchContext = SynchronizationContext.Current;
}
public void SetProgress(double progress, string message)
{
_synchContext.Send((state) =>
{
SplashProgress.Value = progress;
SplashMessage.Content = message;
}, null);
}
}
}
And Finally the BootStrapper Class that I am trying to make the update calls from:
namespace Application
{
internal sealed class BootStrapper : BaseMefBootstrapper
{
private ProgressSplashScreen _splash;
public BootStrapper(ProgressSplashScreen splashScreen)
{
_splash = splashScreen;
}
public void Run()
{
// removed do stuff code here
_splash.SetProgress(10, "Loading stuff...");
// removed do stuff code here
_splash.SetProgress(40, "Loading stuff...");
// removed do stuff code here
_splash.SetProgress(80, "Loading stuff...");
// removed do stuff code here
_splash.SetProgress(90, "Loading stuff...");
// removed do stuff code here
_splash.SetProgress(100, "Loading stuff...");
}
}
}
And so far my screen just sits there with an empty progress bar...
Failed Solution 2
ps... I've also tried
using System;
using System.Threading;
using System.Windows;
namespace Application
{
public partial class ProgressSplashScreen : Window, INotifyPropertyChanged
{
private Dispatcher _dispatcher;
public event PropertyChangedEventHandler PropertyChanged;
private string _progressMessage;
private double _progressValue;
public string ProgressMessage
{
get { return _progressMessage; }
set
{
_progressMessage = value;
OnPropertyChanged("ProgressMessage");
}
}
public double ProgressValue
{
get { return _progressValue; }
set
{
_progressValue = value;
OnPropertyChanged("ProgressValue");
}
}
public ProgressSplashScreen(Action isLoaded)
{
this.Loaded += (sender, args) => isLoaded();
InitializeComponent();
_dispatcher = Dispatcher.CurrentDispatcher;
}
public void SetProgress(double progress, string message)
{
_dispatcher.BeginInvoke(new Action(() =>
{
ProgressValue = progress;
ProgressMessage = message;
}), DispatcherPriority.ContextIdle, null);
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
with this as the XAML
<Window x:Class="Application.ProgressSplashScreen"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Splash" BorderBrush="Transparent"
AllowsTransparency="True"
Icon="../../Resources/Icons/Icon.ico"
WindowStartupLocation="CenterScreen"
WindowStyle="None" Width="640" Height="520"
Background="Transparent" Name="SplashWindow" Topmost="True" >
<Grid>
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<Image Source="../../Resources/Images/splash.jpg" Height="480" Width="640"/>
<Grid>
<ProgressBar Name="SplashProgress" Height="20" Minimum="0" Maximum="100"
BorderBrush="Transparent" Value="{Binding ProgressValue,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<Label Name="SplashMessage" VerticalAlignment="Bottom" HorizontalAlignment="Center"
Content="{Binding ProgressMessage,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</Grid>
</StackPanel>
</Grid>
</Window>
Any help would be great! Thanks! Really surprised this is as hard as it seems right now...
So what I was talking about is :
MainWindow.xaml
<Window x:Class="SplashSxreenExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Splashscreen"
WindowStyle="none" ResizeMode="NoResize" ShowInTaskbar="False" WindowStartupLocation="CenterScreen"
Height="400" Width="700" Background="#FF292929" FontFamily="Century Gothic"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/SplashScrTemplate.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="AUto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Test Splash Screen" Foreground="LightGray" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="60"/>
<Label Grid.Row="1" Content="{Binding ProgressMessage}" Foreground="LightGray" FontStyle="Italic"/>
<ProgressBar Grid.Row="2" Height="3" Foreground="White" Style="{StaticResource FlatProgressBar}"
Value="{Binding ProgressValue}"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
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;
using System.ComponentModel;
namespace SplashSxreenExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _progressMessage;
private int _progressValue;
private BackgroundWorker _mWorker;
public MainWindow()
{
InitializeComponent();
_mWorker = new BackgroundWorker();
_mWorker.WorkerReportsProgress = true; //Allow reporting
_mWorker.ProgressChanged += _mWorker_ProgressChanged;
_mWorker.DoWork += _mWorker_DoWork;
_mWorker.RunWorkerAsync();
}
void _mWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker wkr = sender as BackgroundWorker;
wkr.ReportProgress(0, "Starting..."); //Reporting progress
//Here do your stuff
Thread.Sleep(500);
wkr.ReportProgress(10, "Loading stuff...");
//Here do your stuff
Thread.Sleep(1000);
wkr.ReportProgress(40, "Loading stuff 2...");
//Here do your stuff
Thread.Sleep(500);
wkr.ReportProgress(80, "Loading stuff 3...");
//Here do your stuff
Thread.Sleep(1500);
wkr.ReportProgress(90, "Loading stuff 4...");
//Here do your stuff
Thread.Sleep(2000);
wkr.ReportProgress(100, "Finished");
}
void _mWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ProgressValue = e.ProgressPercentage; //Value
ProgressMessage = e.UserState.ToString(); //Message
}
#region Properties
public string ProgressMessage
{
get { return _progressMessage; }
set
{
_progressMessage = value;
OnPropertyChanged("ProgressMessage");
}
}
public int ProgressValue
{
get { return _progressValue; }
set
{
_progressValue = value;
OnPropertyChanged("ProgressValue");
}
}
#endregion
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
And the resource dictionnary (for the flat bar, just in case)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="FlatProgressBar" TargetType="{x:Type ProgressBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Border BorderBrush="{x:Null}" BorderThickness="0" Background="{x:Null}" CornerRadius="0" Padding="0">
<Grid x:Name="PART_Track">
<Rectangle x:Name="PART_Indicator" HorizontalAlignment="Left" Fill="{Binding RelativeSource={RelativeSource AncestorType=ProgressBar},Path=Foreground}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
This is working pretty well for me, tell me if you have some difficulties.
Here is a ptrscr of the result :
I hope it could help you.
BR,
Bastien.
Related
I am struggling to stop, resume and start the timer using wpf in c# windows application.
I have these button on my xaml but need some logic around c# windows application to achieve this. Secondly, I must have a status bar that will display to the user. When the timer was stop, resume and start as in windows application on my wpf.
How do I create an object in json to serialize this information?
This is my frontend XAML code:
<Window x:Class="PingApplication.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:local="clr-namespace:PingApplication"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="750">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Grid.Row="3" HorizontalAlignment="Right"
MinWidth="80" Margin="3" Content="Start" />
<Button Grid.Column="1" Grid.Row="3" HorizontalAlignment="Left"
MinWidth="80" Margin="3" Content="Stop" />
<Button Grid.Column="1" Grid.Row="3" HorizontalAlignment="Center"
MinWidth="80" Margin="3" Content="Resume" />
<Label Name="lblTime" FontSize="48" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Window>
And this is my c# backend code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
using System.Windows.Threading;
namespace PingApplication
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += timer_Tick;
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
lblTime.Content = DateTime.Now.ToLongTimeString();
}
}
}
`
An example of a timer wrapper and its usage:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Input;
namespace Core2022.SO.Juju22Nimza
{
public class WpfTimer : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private readonly Timer timer = new Timer();
private readonly Stopwatch stopwatch = new Stopwatch();
public DateTime Now => DateTime.Now;
public TimeSpan Elasped => stopwatch.Elapsed;
public WpfTimer()
{
timer.Interval = 50;
timer.Elapsed += OnTick;
timer.Start();
stopwatch.Start();
}
private void OnTick(object? sender, ElapsedEventArgs? e)
{
if (PropertyChanged is PropertyChangedEventHandler propertyChanged)
{
propertyChanged(this, NowArgs);
propertyChanged(this, ElaspedArgs);
}
}
public static PropertyChangedEventArgs NowArgs { get; } = new PropertyChangedEventArgs(nameof(Now));
public static PropertyChangedEventArgs ElaspedArgs { get; } = new PropertyChangedEventArgs(nameof(Elasped));
public static RoutedUICommand StartCommand { get; } = new RoutedUICommand("Timer Start", "TimerStart", typeof(WpfTimer));
public static RoutedUICommand ResumeCommand { get; } = new RoutedUICommand("Timer Resume", "TimerResume", typeof(WpfTimer));
public static RoutedUICommand RestartCommand { get; } = new RoutedUICommand("Timer Restart", "TimerRestart", typeof(WpfTimer));
public static RoutedUICommand ResetCommand { get; } = new RoutedUICommand("Timer Reset", "TimerReset", typeof(WpfTimer));
public static ExecutedRoutedEventHandler ExecuteCommand { get; } = (_, e) =>
{
if (e.Parameter is WpfTimer timer)
{
if (e.Command == StartCommand)
{
timer.stopwatch.Start();
}
else if (e.Command == ResumeCommand)
{
timer.stopwatch.Stop();
}
else if (e.Command == RestartCommand)
{
timer.stopwatch.Restart();
}
else if (e.Command == ResetCommand)
{
timer.stopwatch.Reset();
}
else return;
timer.OnTick(null, null);
}
};
public static CanExecuteRoutedEventHandler CanExecuteCommand { get; } = (_, e) =>
{
if (e.Parameter is WpfTimer timer)
{
if (e.Command == StartCommand)
{
e.CanExecute = !timer.stopwatch.IsRunning;
}
else if (e.Command == ResumeCommand)
{
e.CanExecute = timer.stopwatch.IsRunning;
}
else if (e.Command == RestartCommand)
{
e.CanExecute = true;
}
else if (e.Command == ResetCommand)
{
e.CanExecute = true;
}
}
};
public static WpfTimer Default { get; } = new WpfTimer();
}
}
<Window x:Class="Core2022.SO.Juju22Nimza.WpfTimerWindow"
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:local="clr-namespace:Core2022.SO.Juju22Nimza"
mc:Ignorable="d"
Title="WpfTimerWindow" Height="450" Width="800">
<Window.CommandBindings>
<CommandBinding Command="{x:Static local:WpfTimer.StartCommand}"
Executed="{x:Static local:WpfTimer.ExecuteCommand}"
CanExecute="{x:Static local:WpfTimer.CanExecuteCommand}"/>
<CommandBinding Command="{x:Static local:WpfTimer.ResetCommand}"
Executed="{x:Static local:WpfTimer.ExecuteCommand}"
CanExecute="{x:Static local:WpfTimer.CanExecuteCommand}"/>
<CommandBinding Command="{x:Static local:WpfTimer.RestartCommand}"
Executed="{x:Static local:WpfTimer.ExecuteCommand}"
CanExecute="{x:Static local:WpfTimer.CanExecuteCommand}"/>
<CommandBinding Command="{x:Static local:WpfTimer.ResumeCommand}"
Executed="{x:Static local:WpfTimer.ExecuteCommand}"
CanExecute="{x:Static local:WpfTimer.CanExecuteCommand}"/>
</Window.CommandBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Button Padding="15 5" Margin="5" Content="Start"
Command="{x:Static local:WpfTimer.StartCommand}"
CommandParameter="{x:Static local:WpfTimer.Default}"/>
<Button Padding="15 5" Margin="5" Content="Resume"
Command="{x:Static local:WpfTimer.ResumeCommand}"
CommandParameter="{x:Static local:WpfTimer.Default}"/>
<Button Padding="15 5" Margin="5" Content="Restart"
Command="{x:Static local:WpfTimer.RestartCommand}"
CommandParameter="{x:Static local:WpfTimer.Default}"/>
<Button Padding="15 5" Margin="5" Content="Reset"
Command="{x:Static local:WpfTimer.ResetCommand}"
CommandParameter="{x:Static local:WpfTimer.Default}"/>
</StackPanel>
<StackPanel>
<TextBlock FontSize="48" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Elasped, Source={x:Static local:WpfTimer.Default}}"/>
<TextBlock FontSize="48" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Now, Source={x:Static local:WpfTimer.Default}, StringFormat=T}"/>
</StackPanel>
</Grid>
</Window>
dont know about resume but timer.stop();.resume is might be start to
This user control I am developing was working and now it has quit working and I cannot figure out what changed. (I've closed and re-opened VS2019 several times, it shows no errors when building or running.) For clarity, I've included the pertinent code sections below but I've included all the code at bottom.
MessagePanel.xaml.cs
public string MessageResponse
{
get { return (string)GetValue(MessageResponseProperty); }
set { SetValue(MessageResponseProperty, value); }
}
// Using a DependencyProperty as the backing store for MessageResponse. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MessageResponseProperty =
DependencyProperty.Register("MessageResponse", typeof(string), typeof(MessagePanel), new PropertyMetadata(""));
private void ButtonProceed_Click(object sender, RoutedEventArgs e)
{
MessageResponse = "Proceed";
}
private void ButtonHalt_Click(object sender, RoutedEventArgs e)
{
MessageResponse = "Halt";
}
MessagePanel.xaml
<TextBox Text="{Binding MessageResponse, RelativeSource={RelativeSource AncestorType=UserControl}}" />
MainWindow.xaml
<uc:MessagePanel MessageResponse="{Binding MainMessageResponse}" />
MainVM.cs
private string mainMessageResponse;
public string MainMessageResponse
{
get => mainMessageResponse;
set
{
mainMessageResponse = value;
NotifyPropertyChanged();
}
}
As far as I can tell, the DependencyProperty MessageResponse in MessagePanel should be propagated to the MainMessageResponse property in the view model MainVM.cs. Certainly, if I insert code in the view model to set the MainMessageResponse value, the NotifyPropertyChanged() fires and the value appears in the bound TextBox in MainWindow.xaml. But when I click on either button of the user control, though the value appears in the bound TextBox in MessagePanel.xaml, the value no longer propagates through to MainMessageResponse.
What am I missing here?
Full code follows (stripped to the bare necessities):
MessagePanel.xaml
<UserControl x:Class="Common.UserControls.MessagePanel"
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"
d:DesignHeight="100" d:DesignWidth="400"
Visibility="Visible" >
<Grid>
<Border
MinWidth="50" MinHeight="50"
Background="LightCoral"
BorderBrush="Black"
BorderThickness="1"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<StackPanel>
<StackPanel Width="400" Margin="5,5,5,0">
<TextBox x:Name="Title" TextWrapping="Wrap" MinHeight="16" Background="LightPink" HorizontalAlignment="Center" />
<TextBox x:Name="Message" IsReadOnly="True" TextWrapping="Wrap" MinHeight="42" Background="LightPink" HorizontalAlignment="Stretch" />
</StackPanel>
<DockPanel >
<CheckBox x:Name="CheckBoxConfirm" Checked="CheckBoxConfirm_Checked" Unchecked="CheckBoxConfirm_Unchecked" FontStyle="Italic" FontWeight="Bold" HorizontalAlignment="Center" />
</DockPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Name="ButtonProceed" Click="ButtonProceed_Click" Width="50" Margin="5,5" />
<Button x:Name="ButtonHalt" Click="ButtonHalt_Click" Width="50" Margin="5,5" />
</StackPanel>
<TextBox Visibility="Visible" Name="Response" Text="{Binding MessageResponse, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</StackPanel>
</Border>
</Grid>
</UserControl>
MesssagePanel.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace Common.UserControls
{
/// <summary>
/// Interaction logic for MessagePanel.xaml
/// </summary>
public partial class MessagePanel : UserControl
{
public enum MessageType
{
Ok,
OkCancel,
YesNo
}
public MessagePanel()
{
InitializeComponent();
Title.Text = "This is a title";
Message.Text = "This is a test message with title and [Yes] and [No] buttons and requires a confirmation.";
ButtonProceed.Content = "Yes";
ButtonHalt.Content = "No";
CheckBoxConfirm.Visibility = Visibility.Collapsed;
}
#region Dependeny Properties
public string MessageResponse
{
get { return (string)GetValue(MessageResponseProperty); }
set { SetValue(MessageResponseProperty, value); }
}
// Using a DependencyProperty as the backing store for MessageResponse. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MessageResponseProperty =
DependencyProperty.Register("MessageResponse", typeof(string), typeof(MessagePanel), new PropertyMetadata(""));
#endregion
#region Event Handlers
private void ButtonProceed_Click(object sender, RoutedEventArgs e)
{
// User wants to proceed
MessageResponse = "Proceed";
}
private void ButtonHalt_Click(object sender, RoutedEventArgs e)
{
// User wants to not proceed
MessageResponse = "Halt";
}
private void CheckBoxConfirm_Checked(object sender, RoutedEventArgs e)
{
ButtonProceed.IsEnabled = true;
}
private void CheckBoxConfirm_Unchecked(object sender, RoutedEventArgs e)
{
ButtonProceed.IsEnabled = false;
}
#endregion
}
}
MainWindow.xaml
<Window x:Class="WpfUserControl.Views.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:uc="clr-namespace:Common.UserControls;assembly=Common"
xmlns:local="clr-namespace:WpfUserControl"
mc:Ignorable="d"
Title="Demo"
Height="300" Width="600">
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="50" />
<RowDefinition Height="50" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBox Text="{Binding MainMessageResponse}" Width="50" Height="22" />
</StackPanel>
</Grid>
<uc:MessagePanel MessageResponse="{Binding MainMessageResponse}" />
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
using WpfUserControl.ViewModels;
namespace WpfUserControl.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainVM vm = new MainVM();
DataContext = vm;
}
}
}
MainVM.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfUserControl.ViewModels
{
public partial class MainVM : INotifyPropertyChanged
{
public MainVM()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#region MessagePanel
private string mainMessageResponse;
public string MainMessageResponse
{
get => mainMessageResponse;
set
{
mainMessageResponse = value;
NotifyPropertyChanged();
}
}
#endregion
}
}
You should set the Mode of the Binding to TwoWay. You can do this for an individual binding in the XAML markup:
<uc:MessagePanel MessageResponse="{Binding MainMessageResponse, Mode=TwoWay}" />
Or you can specify the default value for all bindings when you register the dependency property in the control:
public static readonly DependencyProperty MessageResponseProperty =
DependencyProperty.Register("MessageResponse", typeof(string), typeof(MessagePanel),
new FrameworkPropertyMetadata("") { BindsTwoWayByDefault = true });
I need pop up a window which takes time. The button is in Pressed state until the new window is opened. Hence I want to add a wait indicator over the UI window after I click the button and before the the window opens. The code of ViewModel is correct because I referred to a sample code. But why there is no response after I click the button.
The project file URL
https://supportcenter.devexpress.com/attachment/file/5268961b-ce35-4e40-b7c1-e33bffab902b
MainWindow:
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;
using DevExpress.Xpf.Core;
namespace WaitIndicatorDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : DXWindow
{
public MainWindow()
{
InitializeComponent();
vm = new MainVM();
DataContext = vm;
}
MainVM vm;
private void buttonShow_Click(object sender, RoutedEventArgs e)
{
vm.IsBusy = !vm.IsBusy;
}
}
}
ViewMode:
using DevExpress.Mvvm;
namespace WaitIndicatorDemo
{
public class MainVM
{
private readonly ISplashScreenService _waitIndicatorService;
public virtual bool IsBusy { get; set; }
public MainVM()
{
_waitIndicatorService =
ServiceContainer.Default.GetService<ISplashScreenService>("WaitIndicatorService");
}
protected void OnIsBusyChanged()
{
if (IsBusy)
_waitIndicatorService.ShowSplashScreen();
else
_waitIndicatorService.HideSplashScreen();
}
}
}
Below is the XAML, the comment ones are the original sample code. The checkbox bind to IsBusy. The indicator pop up when the checkbox is checked. I now want to pop up after press the button.
<dx:DXWindow x:Class="WaitIndicatorDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
xmlns:waitIndicatorDemo="clr-namespace:WaitIndicatorDemo"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
WindowStartupLocation="CenterScreen" SnapsToDevicePixels="True"
Title="MainWindow" Height="350" Width="525">
<!--DataContext="{dxmvvm:ViewModelSource Type=waitIndicatorDemo:MainVM}"-->
<!--Title="MainWindow" Height="350" Width="525">-->
<Grid Margin="10">
<!--<dxe:CheckEdit Content="Is Busy" IsChecked="{Binding IsBusy}"
VerticalAlignment="Top" HorizontalAlignment="Left" />
<Button Content="Button1" IsEnabled ="{Binding IsBusy, Converter={dxmvvm:BooleanNegationConverter}}"
VerticalAlignment="Top" HorizontalAlignment="Center" Click="Button_Click"/>
<Button Content="Button2" IsEnabled="{Binding IsBusy, Converter={dxmvvm:BooleanNegationConverter}}"
VerticalAlignment="Top" HorizontalAlignment="Right"/>-->
<Button x:Name="buttonShow" Content="Show" HorizontalAlignment="Left" Height="35" Margin="50,70,0,0" VerticalAlignment="Top" Width="75" Click="buttonShow_Click" />
</Grid>
</dx:DXWindow>
You have a few mistakes in your code sample :
1- The method OnIsBusyChanged in your ViewModel is never called.
2- Your XAML doesn't declare any ISplashScreenService object in the Window behaviors, like a DXSplashScreenService for instance.
Here's how you can fix both of those issues.
First, fix the ViewModel.
public class MainVM
{
private readonly ISplashScreenService _waitIndicatorService;
private bool _isBusy;
public virtual bool IsBusy
{
get
{
return _isBusy;
}
set
{
_isBusy = value;
OnIsBusyChanged();
}
}
public MainVM()
{
_waitIndicatorService =
ServiceContainer.Default.GetService<ISplashScreenService>("WaitIndicatorService");
}
protected void OnIsBusyChanged()
{
_waitIndicatorService.SetSplashScreenState("Doing some work...");
if (IsBusy)
_waitIndicatorService.ShowSplashScreen();
else
_waitIndicatorService.HideSplashScreen();
}
}
Then, your XAML.
<dx:DXWindow x:Class="WaitIndicatorDemo.MainWindow"
<!-- ... -->
Title="MainWindow" Height="350" Width="525">
<dxmvvm:Interaction.Behaviors>
<dx:DXSplashScreenService x:Name="WaitIndicatorService">
<dx:DXSplashScreenService.ViewTemplate>
<DataTemplate>
<Grid>
<Border Background="LightGray" CornerRadius="5">
<Border BorderBrush="#FF0072C6" BorderThickness="1" Margin="15" CornerRadius="5">
<Grid>
<ProgressBar BorderThickness="0" Value="{Binding Progress}" Maximum="{Binding MaxProgress}" IsIndeterminate="{Binding IsIndeterminate}" Height="12" />
<TextBlock Text="{Binding State, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Border>
</Border>
</Grid>
</DataTemplate>
</dx:DXSplashScreenService.ViewTemplate>
</dx:DXSplashScreenService>
</dxmvvm:Interaction.Behaviors>
<Grid Margin="10">
<!-- ... -->
</Grid>
</dx:DXWindow>
This code was mainly taken from the How to: Use DxSplashScreenService sample.
I am creating a program where if you click the button it adds a new label. However the problem is that when you click the add button the label keeps getting stack on top of eachother instead of being a list
Here is the code
c#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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 firstwpfapp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void addTask(object sender, RoutedEventArgs e)
{
String val = input.ToString();
Label todo = new Label();
todo.Content = val;
List.Children.Add(todo);
}
}
}
xaml
...
<Window x:Class="firstwpfapp.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:local="clr-namespace:firstwpfapp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Margin="-120,-142,0,0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0*"/>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="451*"/>
</Grid.ColumnDefinitions>
<StackPanel x:Name="Wrapper"Background="LightGray" Orientation="Horizontal"></StackPanel>
<TextBox x:Name="input" Grid.Column="2" HorizontalAlignment="Left" Height="31" Margin="106,198,0,0" TextWrapping="Wrap" Text="Enter here" VerticalAlignment="Top" Width="166"/>
<Button Content="Button" Grid.Column="2" HorizontalAlignment="Left" Margin="106,234,0,0" VerticalAlignment="Top" Width="166" Height="26" Click="addTask"/>
<Grid x:Name="List" Grid.Column="2" HorizontalAlignment="Left" Height="391" Margin="507,160,0,0" VerticalAlignment="Top" Width="385"/>
</Grid>
</Window>
the list keeps getting stack on top of another each time the button is pressed
You can go ahead and add your items to a ListView which will stack the items for you as well as include an ItemSource that we can bind to so it will create the rows for each new ToDo item for you:
Note: I have not tested the below; I'm on my Macbook.
Instead of your Wrapper StackLayout, replace it with:
<ListView Name="Wrapper" Grid.Row="0" Grid.ColumnSpan="3" ItemsSource="{Binding Tasks}" />
Now, create a new file called Task.cs which we will use when creating a new type of Task (add the below to the Task.cs file):
public class Task { public string task { get; set;} }
Have your MainWindow inherit from the INotifyPropertyChanged interface INotifyPropertyChanged
public partial class MainWindow : Window, INotifyPropertyChanged
Now update the rest of your code behind of MainWindow to:
private ObservableCollection<Task> _tasks;
//Tasks will store all of the tasks of type Task that we add to it
//as well as be bound to our ListView that will display whatever we add to our Tasks
public ObservableCollection<Task> Tasks
{
get
{
return _tasks;
}
set
{
if (value != _tasks)
{
_tasks = value;
OnPropertyChanged("Tasks");
}
}
}
//Here we implement OnPropertyChanged so our ObservableCollection can be notified
//whenever we have a new task added to or removed from Tasks (this is created when we implement INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
//Create a new Task and add it to our Tasks any time the addTask button is clicked
private void addTask(object sender, RoutedEventArgs e)
{
Tasks.Add(new Task(input.Text));
}
Replace the Grid
<Grid x:Name="List" Grid.Column="2" HorizontalAlignment="Left" Height="391" Margin="507,160,0,0" VerticalAlignment="Top" Width="385"/>
with a StackPanel:
<StackPanel x:Name="List" Grid.Column="2" HorizontalAlignment="Left" Height="391" Margin="507,160,0,0" VerticalAlignment="Top" Width="385"/>
Adding child elements to a grid stacks them on top of each other (as you have noticed)
A StackPanel adds new child elements below (or after) the previous child element.
Try this ...................
XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Center"
Orientation="Horizontal"
Margin="0,50,0,0">
<TextBox Name="Input"
Width="300"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<Button Name="AddBtn"
Content="Add"
Margin="20,0,0,0"
VerticalAlignment="Center"
Width="100"
Click="AddBtn_Click"/>
</StackPanel>
<ListView Name="ItemListView"
ItemsSource="{Binding Path=LabelItems, Mode=OneWay}"
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Margin="20"/>
</Grid>
C# code:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private ObservableCollection<string> _labels = new ObservableCollection<string>();
public ObservableCollection<string> LabelItems
{
get { return _labels; }
set { _labels = value; RaisePropertyChanged(); }
}
private void AddBtn_Click(object sender, RoutedEventArgs e)
{
if(Input.Text != "" && Input.Text != null)
{
LabelItems.Add(Input.Text);
}
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged([CallerMemberName]string name = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
I am trying to modify an existing code which allows to add articles by rows and remove them wherever needed, the problem is that I am trying to find a way to group articles of the same category. So when the user adds a new article of DVD category, it will be directly added to that category (I tried to take some ideas from here, but with no success: http://msdn.microsoft.com/en-us/library/ff407126.aspx
This is the code behind data:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfDataGridWithDataTable
{
public class Article
{
public Article ()
{
}
private int _modelNumber;
public int ModelNumber
{
get { return _modelNumber; }
set { _modelNumber = value; OnPropertyChanged("ModelNumber"); }
}
private string _modelName;
public string ModelName
{
get { return _modelName; }
set { _modelName = value; OnPropertyChanged("ModelName"); }
}
private decimal _unitCost;
public decimal UnitCost
{
get { return _unitCost; }
set { _unitCost = value; OnPropertyChanged("UnitCost"); }
}
private string _description ;
public string Description
{
get { return _description; }
set { _description = value; OnPropertyChanged("Description"); }
}
#region INotifyPropertyChanged Membres
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
}
public class ListArticles : ObservableCollection<Article >
{
public Article a;
public ListArticles()
{
a = new Article();
this.Add(a);
}
}
}
And here is the XAML code:
<Window x:Class="WpfDataGridWithDataTable.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfDataGridWithDataTable"
Title="Window1" Height="300" Width="300">
<Grid
Name="gridPanel">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<DataGrid
Grid.Column="0"
Name="dataGrid1"
AutoGenerateColumns="True"
CanUserAddRows="True"
CanUserDeleteRows="True"
CanUserResizeColumns="True"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"/>
<ListBox
Grid.Column="1"
Name="listBox1"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:Article}">
<StackPanel
Orientation="Horizontal">
<TextBlock
Width="100"
Margin="10"
Background="DarkBlue"
Foreground="White"
FontSize="14"
Text="{Binding ModelNumber}"/>
<TextBlock
Width="100"
Margin="10"
Background="DarkBlue"
Foreground="White"
FontSize="14"
Text="{Binding ModelName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Left"
Width="100"
Name="btnAdd"
Content="Add Item"
Click="btnAdd_Click">
</Button>
<Button
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Right"
Width="100"
Name="btnDelete"
Content="Delete Item"
Click="btnDelete_Click" >
</Button>
</Grid>
</Window>
And the code behind the form:
namespace WpfDataGridWithDataTable
{
/// <summary>
/// Logique d'interaction pour Window1.xaml
/// </summary>
public partial class Window1 : Window
{
private ListArticles myList;
public Window1()
{
InitializeComponent();
myList = new ListArticles();
this.DataContext = myList;
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
myList.Add(new Article());
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
myList.Remove(this.dataGrid1.SelectedItem as Article);
}
}
}
Thanks for your answers.
I finally resolved the problem, but I don't know why it didn't worked. The issue was located in the XAML code when binding the datagrid with the CollectionViewSource. I put:
<DataGrid Name="dataGrid1"
AutoGenerateColumns="True"
CanUserAddRows="True"
CanUserDeleteRows="True"
CanUserResizeColumns="True"
IsSynchronizedWithCurrentItem="True"
Grid.ColumnSpan="2"
ItemsSource="{Binding Source={StaticResource cvsListArticles}}">
The last line was the problem, when I simply put ItemsSource="{Binding}" it finally worked.
Here is the code for those who might have the same problem and want to benefit from it:
code behind data:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace WpfDataGridWithDataTable
{
public class Article
{
public Article()
{
}
private int _modelNumber;
public int ModelNumber
{
get { return _modelNumber; }
set { _modelNumber = value; OnPropertyChanged("ModelNumber"); }
}
private string _modelName;
public string ModelName
{
get { return _modelName; }
set { _modelName = value; OnPropertyChanged("ModelName"); }
}
private decimal _unitCost;
public decimal UnitCost
{
get { return _unitCost; }
set { _unitCost = value; OnPropertyChanged("UnitCost"); }
}
private string _description;
public string Description
{
get { return _description; }
set { _description = value; OnPropertyChanged("Description"); }
}
#region INotifyPropertyChanged Membres
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
}
public class ListArticles : ObservableCollection<Article>
{
public Article a;
public ListArticles()
{
a = new Article();
this.Add(a);
}
}
}
The XAML code:
<Window x:Class="WpfDataGridWithDataTable.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication18"
Title="Window1" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Height="480" Width="760">
<Window.Resources>
<CollectionViewSource x:Key="cvsListArticles" Source="Article">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="ModelName"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid
Name="gridPanel">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<DataGrid Name="dataGrid1"
AutoGenerateColumns="True"
CanUserAddRows="True"
CanUserDeleteRows="True"
CanUserResizeColumns="True"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}" Grid.ColumnSpan="2" >
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" Background="#FF112255" BorderBrush="#FF002255" Foreground="#FFEEEEEE" BorderThickness="1,1,1,5">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0" Width="100"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
</DataGrid>
<Button
Grid.Row="1"
Grid.Column="0"
HorizontalAlignment="Left"
Width="100"
Name="btnAdd"
Content="Add Item"
Click="btnAdd_Click">
</Button>
<Button
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Right"
Width="100"
Name="btnDelete"
Content="Delete Item"
Click="btnDelete_Click" >
</Button>
<Button Content="Group" Grid.ColumnSpan="2" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="294,5,0,0" Name="GroupButton" VerticalAlignment="Top" Width="145" Click="GroupButton_Click" />
</Grid>
</Window>
The code behind the form:
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.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace WpfDataGridWithDataTable
{
public partial class Window1 : Window
{
private ListArticles myList;
public Window1()
{
InitializeComponent();
myList = new ListArticles();
this.DataContext = myList;
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
myList.Add(new Article());
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
myList.Remove(this.dataGrid1.SelectedItem as Article);
}
private void GroupButton_Click(object sender, RoutedEventArgs e)
{
ICollectionView cvsListArticles = CollectionViewSource.GetDefaultView(dataGrid1.ItemsSource);
if (cvsListArticles != null && cvsListArticles.CanGroup == true)
{
cvsListArticles.GroupDescriptions.Clear();
cvsListArticles.GroupDescriptions.Add(new PropertyGroupDescription("ModelName"));
}
}
}
}