WpfDatagrid Collectionviewsource clear - wpf

I am using wpftoolkit datagrid control which was binding using collectionviewsource for records grouping. Whenever user trying to clear the form i need to clear the datagrid also.
I tried to set datagrid itemsource to null but it works fine for clear functionality .if user trying to add any records to datagrid it's not loading.
So could any one please provide me a solution to clear the datagrid.
Thanks In Advance.

I use ObservableCollection for this type of situations.
Example:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.items = new ObservableCollection<Item>(
Enumerable.Range(0, 10).Select(i => new Item {Id = i, Title = "str " + i}));
this.viewSource = new CollectionViewSource() { Source = this.items };
dataGrid1.ItemsSource = this.viewSource.View;
}
private ObservableCollection<Item> items;
private CollectionViewSource viewSource;
private void Button_Click(object sender, RoutedEventArgs e)
{
items.Clear();
//or
//((ObservableCollection<Item>)viewSource.Source).Clear();
}
public class Item
{
public int Id { get; set; }
public string Title { get; set; }
}
}
Xaml:
<Button HorizontalAlignment="Center" Content="Clear" Click="Button_Click"/>
<DataGrid AutoGenerateColumns="True" Name="dataGrid1" />

Related

WPF how to transfer data between windows (MVVM)?

I know there are a lot of similar questions and I spent two hours by now trying to implementing them but can't proceed. So the problem seems simple. When I don't have a viewmodel, I can set the datacontext to a class and it is very easy to transfer data with that class. But when there is viewmodel, I have to set the datacontext to that and can't find a way to return any value after that. I tried to implement countless solutions to the problem but it seems that they are above my skill level. Thank you so much for your help!
The important parts of my code (its a simple game which i want to save, where save is named by userinput) The first window, where I want to get data from the second window
case Key.Escape: {
Thread t = new Thread(() => {
SaveGameWindow pw = new SaveGameWindow(); //the second window
if ((pw.ShowDialog() == true) && (pw.Name != string.Empty)) //pw.Name always empty
{
ILogicSaveGame l = new LogicSaveGame();
l.Write(pw.Name, "saved_games.txt");
MessageBox.Show("game saved");
}
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
XAML (from now on everything belongs to the SaveGameWindow):
<Window.Resources>
<local:SaveGameViewModel x:Key="my_viewmodel"/>
</Window.Resources>
<Grid DataContext="{StaticResource my_viewmodel}">
<TextBox Text="{Binding Name}"/> //i want to acces this in the first window
<Button Command="{Binding CloseCommand}"
Content="Save"/>
Code behind:
private readonly SaveGameViewModel vm;
public SaveGameWindow()
{
this.InitializeComponent();
this.vm = this.FindResource("my_viewmodel") as SaveGameViewModel;
if (this.vm.CloseAction == null)
{
this.vm.CloseAction = new Action(() => this.Close());
}
}
Viewmodel
public class SaveGameViewModel : ViewModelBase
{
public SaveGameViewModel()
{
this.CloseCommand = new RelayCommand(() => this.Close());
}
public string Name { get; set; }
public ICommand CloseCommand { get; private set; }
public Action CloseAction { get; set; }
private void Close()
{
this.CloseAction();
}
}
I use galasoft mvvmlightlibs
There are many solutions to this problem. The simplest solution is to use a shared view model for both windows and data binding. Since both windows would share the same DataContext, both have access to the same data or model instance by simply referencing their DataContext property.
But if you prefer to have individual view models, you would choose a different solution.
Solution 1
If you want to use a dedicated view model for each window, you can always use composition and make e.g. an instance SaveGameViewModel a member of MainWindowViewModel. Any class that has access to MainWindowViewModel will also have access to the SaveGameViewModel and its API, either directly or via delegating properties.
This example uses direct access by exposing SaveGameViewModel as a public property of MainWindowViewModel:
SaveGameViewModel.cs
public class SaveGameViewModel : INotifyPropertyChanged
{
private string name;
public string Name
{
get => this.name;
set
{
this.name = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindowViewModel.cs
public class MainWindowViewModel : INotifyPropertyChanged
{
public SaveGameViewModel SaveGameViewModel { get; set; }
// Allow to create an instance using XAML
public MainWindowViewModel() {}
// Allow to create an instance using C#
public MainWindowViewModel(SaveGameViewModel saveGameViewModel)
=> this.SaveGameViewModel = saveGameViewModel;
}
App.xaml
<Application>
<Application.Resources>
<MainWindowViewModel x:Key="MainWindowViewModel">
<MainWindowViewModel.SaveGameViewModel>
<SaveGameViewModel />
</MainWindowViewModel.SaveGameViewModel>
</MainWindowViewModel>
</Application.Resources>
</Application>
SaveGameWindow.xaml
<Window DataContext="{Binding Source={StaticResource MainWindowViewModel}, Path=SaveGameViewModel}">
<TextBox Text="{Binding Name}" />
<Window>
MainWindow.xaml
<Window DataContext="{StaticResource MainWindowViewModel}">
<Window>
MainWindow.xaml.cs
partial class MainWindow : Window
{
private void OnKeyPressed(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
var mainWindowViewModel = this.DataContext as MainWindowViewModel;
string saveGameName = mainWindowViewModel.SaveGameViewModel.Name;
}
}
}
Solution 2
Since you are just showing a dialog, you can store the current instance of the SaveGameViewModel or its values of interest after the dialog has been closed:
MainWindow.xaml.cs
partial class MainWindow : Window
{
private SaveGameViewModel CurrentSaveGameViewModel { get; set; }
private bool IsSaveGameValid { get; set; }
private void ShowDialog_OnSaveButtonClick(object sender, RoutedEventArgs e)
{
var saveGameDialog = new SaveGameWindow();
this.IsSaveGameValid = saveGameDialog.ShowDialog ?? false;
this.CurrentSaveGameViewModel = saveGameDialog.DataContext as SaveGameViewModel;
}
private void OnKeyPressed(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape && this.IsSaveGameValid)
{
string saveGameName = this.CurrentSaveGameViewModel.Name;
}
}
}
MainWindow.xaml
<Window>
<Window.DataContext>
<MainWindowViewModel />
</Window.DataContext>
<Window>
SaveGameWindow.xaml
<Window>
<Window.DataContext>
<SaveGameViewModel />
</Window.DataContext>
<TextBox Text="{Binding Name}" />
<Window>

how to add row in datagrid dynamically

I am using WPF application
in this form have one gridview and one button
i am using the ObservableCollection have Generic and one class code like this
public partial class MainWindow : Window
{
public ObservableCollection<gm> data1 = new ObservableCollection<gm>();
public MainWindow()
{
InitializeComponent();
}
// public ObservableCollection<gm> data { get { return data1; } }
private void button1_Click_1(object sender, RoutedEventArgs e)
{
data1.Add(new gm() { no = 2, name = "vipul" });
dataGrid1.ItemsSource = data1.ToArray();
}
}
public class gm
{
public int no { get; set; }
public string name { get; set; }
}
}
when i am execute above code it add blank row in datagrid
please give me solution of this problem
i want to know how to add row in datagird run time.
thanks in advance
set autogeneratecolumns to true and just set data1 as itemssource
xaml
<DataGrid x:Name="dataGrid1" AutoGenerateColumns="True" />
EDIT: to get the power of WPF look into DataBinding/MVVM
XAML is like this:
<DataGrid ItemsSource="{Binding}" />
code behind is this:
public MainWindow()
{
InitializeComponent();
DataContext = data1;
}
and remove this line:
dataGrid1.ItemsSource = data1.ToArray();
data1.Add(new gm { no = 2, name = "vipul" }); // Remore ellipsis around gm.
In XAML just check the following that
You have DataSource of your datagrid to {Binding Path="data1"}
DataColumn of no have binding to {Binding Path="no"}
DataColumn of name have binding path to {Binding Path="name"}

wpf datagrid icollectionview sorting BUG?

maybe someone can help me? I have the following scenario:
A simple view:
<Window x:Class="DataGridSortBug.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<StackPanel DockPanel.Dock="Top">
<Button Click="Button_Click">Refresh</Button>
</StackPanel>
<DataGrid ItemsSource="{Binding View}" />
</DockPanel>
</Window>
The code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
public class TestItem
{
private int _sequence;
public int Sequence
{
get { return _sequence; }
}
public TestItem(int sequence)
{
_sequence = sequence;
}
}
public class ViewModel
{
ObservableCollection<TestItem> _collection;
private ICollectionView _view;
public ICollectionView View
{
get { return _view; }
}
public ViewModel()
{
_collection = new ObservableCollection<TestItem>();
_collection.Add(new TestItem(5));
_collection.Add(new TestItem(2));
_collection.Add(new TestItem(4));
_collection.Add(new TestItem(3));
_collection.Add(new TestItem(1));
_view = CollectionViewSource.GetDefaultView(_collection);
_view.SortDescriptions.Add(new SortDescription("Sequence", ListSortDirection.Ascending));
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
DataContext = new ViewModel();
}
}
After the program startup the datagrid contains (as expected):
1
2
3
4
5
After click on the button:
5
2
4
3
1
But I really can't understand why. Am I doing something wrong or is this a bug? And if this is a bug is there a workaround?
I just ran into this bug. (Or at least I presume it is a bug).
When debugging, you can see that the SortDescriptions collection gets cleared after assigning the ViewModel to the DataContext.
As a work around, I removed the SortDescriptions from the CTOR of the ViewModel and put them within a public method which I then call after assigning the ViewModel to the DataContext.
private void Button_Click(object sender, RoutedEventArgs e)
{
var model = new ViewModel();
DataContext = model; // SortDescriptions collection is cleared here.
model.AddSortDescriptions();
model.View.Refresh();
}
It is far from ideal, however this seems to be the only workaround I could find.
Try calling
_view.Refresh();
after adding the SortDescription.
Your TestItem is not implementing the IComparable interface so it is not sure of what to compare your objects by.
MSDN IComparable
Basically you need to add this to your class below.
public class TestItem : IComparable
{
private int _sequence;
public int Sequence
{
get { return _sequence; }
}
public TestItem(int sequence)
{
_sequence = sequence;
}
public int CompareTo(object obj)
{
if (obj == null)
return 1;
// put comparison logic here
}

Creating a dynamic checkboxes against the list of items using mvvm light wpf?

I have the following scenario:
I have one window say MainWindow where I am displaying the list of activities as per the specific user from the database. There is a one button present on the window. By clicking on that button a new window is getting opened having all the list of activities from the master table. Now I want add a chechbox on the second window against each item dynamically so that user can select/deselect the activities. Those selected/deselected values should save in the database and Parent/MainWindow should refreshed after clicking on the done button and changes should reflect in the MianWindow. But I am not getting how to dynamically creating the checkboxes against each list item and binding with the xaml and select/deselect the checkbox.
Kindly suggest with samples or examples.
Thanks
You can customize your listviewitem using the ListView's ItemTemplate. Add a checkbox
and a textblock to a panel which would constitute your datatemplate.
Update
The Model:
public class Activity
{
public Activity(int id, string name)
{
ID = id;
Name = name;
}
public int ID { get; set; }
public string Name { get; set; }
}
The ViewModel for ListViewItem in Second Window:
public class ActivityViewModel
{
Activity _model;
public ActivityViewModel(Activity model, bool isSelected)
{
_model = model;
IsSelected = isSelected;
}
public string Name { get { return _model.Name; } }
/* Since the view has a checkbox and it requires a bool value for binding
we create this property */
public Nullable<bool> IsSelected { get; set; }
}
The DataAccess
public class DaoDailyActivities
{
string activityName = "";
bool IsSelected;
SqlConnection con = new SqlConnection("server=172.16.32.68;database=ParentalHealth;uid=sa;pwd=Emids123");
public IEnumerable<Activity> GetActivities()
{
SqlCommand cmd = new SqlCommand("SP_GetActivities", con);
cmd.CommandType = CommandType.StoredProcedure;
con.Open(); /* It is safe to open connections in a try block */
SqlDataReader readerActivities = cmd.ExecuteReader();
while (readerActivities.Read())
{
yield new Activity(readerActivities["ActivityID"].ToString(), readerActivities["ActivityName"].ToString());
}
}
}
The ViewModel for SecondWindow:
public class SecondWindowViewModel : ViewModelBase
{
DaoDailyActivities _rep = new DaoDailyActivities();
public ObservableCollection<ActivityViewModel> AllActivities { get; set; }
public SecondWindowViewModel()
{
LoadAllActivities();
}
LoadAllActivities()
{
foreach(Activity activity in _rep.GetActivities())
{
AllActivities.Add(new ActivityViewModel(activity, (activity.ID % 2 == 0)));
}
}
}
The XAML:
<ListView ItemsSource="{Binding AllActivities}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
<CheckBox IsChecked="{Binding Path=IsSelected}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListView>

WPF User Control

I want to have a User Control that takes a collection of People (property "Data") and displays them in a list box.
When I run my app nothing shows in the listbox. Can you please point out what I'm doing wrong?
Thanks!!!
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return Name + "(" + Age + ")";
}
}
User Control:
(uc1.xaml.cs)
public partial class uc1
{
public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof (List<Person>), typeof (uc1));
public List<Person> Data
{
get { return (List<Person>) GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public uc1()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
DataContext = Data;
}
}
(uc1.xaml)
<ListBox ItemsSource="{Binding Name}" />
The ItemsSource property controls the list of items that are displayed in the listbox. If you want the ListBox to display one line for each person, you need to set the ItemsSource to bind directly to the DataContext. Then you use the DisplayMemberPath property to control which property of the Person class to show.
Here's my sample code that works for me.
The person class is the same.
The Window1.xaml.cs:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
List<Person> Data = new List<Person>();
Data.Add(new Person { Name = "Test 1", Age = 5 });
Data.Add(new Person { Name = "Test 2", Age = 10 });
this.DataContext = Data;
}
}
The Window1.xaml
<ListBox ItemsSource="{Binding}" DisplayMemberPath="Name" />

Resources