I want to bind DataPager to DataGrid
here is xaml
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="NorthWindSilver.MainPage"
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:mv="clr-namespace:NorthWindSilver.ViewModel"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<mv:ViewModel x:Key="ViewModel"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<data:DataGrid Name="dgCustomer" AutoGenerateColumns="True" ItemsSource="{Binding Items, Mode=TwoWay, Source={StaticResource ViewModel}}">
</data:DataGrid>
<sdk:DataPager HorizontalContentAlignment="Center" x:Name="myPager" Grid.Row="2" Source="{Binding Path=ItemsSource, ElementName=dgCustomer}" PageSize="10"/>
</Grid>
and ViewModel
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Customer> _items;
public ViewModel()
{
if (!IsDesignTime)
this.Customer();
}
public void ChangeProperty(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public ObservableCollection<Customer> Items
{
get
{
return this._items;
}
set
{
this._items = value;
ChangeProperty("Items");
}
}
public bool IsDesignTime
{
get
{
return (Application.Current == null) ||
(Application.Current.GetType() == typeof(Application));
}
}
public void Customer()
{
DataServiceClient webService = new DataServiceClient();
webService.GetCustomersCompleted += new EventHandler<GetCustomersCompletedEventArgs>(webService_GetCustomersCompleted);
webService.GetCustomersAsync();
}
void webService_GetCustomersCompleted(object sender, GetCustomersCompletedEventArgs e)
{
Items = e.Result;
PagedCollectionView pageView = new PagedCollectionView(Items);
MainPage ma = new MainPage();
ma.dgCustomer.ItemsSource = pageView;
}
}
Here is the result
As you see DataPager does not work
what the problem?
Try to expose your PagedCollectionView as a property on the ViewModel and bind the ItemsSource on the DataGrid and Pager to the PagedCollectionView instead.
Related
I'm a newbie in wpf and i know that this question has been asked other times, and i tried to implement some solutions that i found. But it's not working. I'm doing something wrong but i can't see what it is.
I've created a new simple application to test this problem.
namespace WpfApp3
{
public class MyElement
{
public string Text { get; set; }
public MyElement(string t)
{
Text = t;
}
}
public class MyCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_handler(parameter);
}
private Action<object> _handler;
public MyCommand(Action<object> handler) { _handler = handler; }
}
public class MyItemsControlViewModel
{
ObservableCollection<MyElement> _items;
public ObservableCollection<MyElement> MyElementItems { get { return _items; } set { _items = value; RaisePropertyChanged("MyElementItems"); } }
ObservableCollection<MyElement> _temporayList;
private ICommand _itemClicked;
public ICommand ItemClicked { get { return _itemClicked; } }
public MyItemsControlViewModel()
{
_items = new ObservableCollection<MyElement>();
_temporayList = new ObservableCollection<MyElement>();
_itemClicked = new MyCommand(OnItemSelected);
AddItem("Element 1");
AddItem("Element 2");
AddItem("Element 3");
UpdateList();
}
public void UpdateList()
{
MyElementItems = _temporayList;
}
public void AddItem(string t)
{
MyElement item = new MyElement(t);
_temporayList.Add(item);
}
public void OnItemSelected(object param)
{
Debug.WriteLine("Executed!");
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML
<UserControl x:Class="WpfApp3.MyUserControl"
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"
xmlns:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="1080" d:DesignWidth="570"
x:Name="myCustomControl">
<Grid >
<Button x:Name="btnOutsideItemsControl" Width="100" Height="100 " VerticalAlignment="Top" Command="{Binding ItemClicked}" />
<ItemsControl
x:Name="listItems"
ScrollViewer.PanningMode="None"
IsEnabled="False"
Background = "Transparent"
HorizontalAlignment="Center"
ItemsSource="{Binding MyElementItems}" Margin="0,152,0,0" Width="549">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" Margin="50,0,0,0"
Background="Transparent" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Content="{Binding Text}"
Command="{Binding ElementName=listItems, Path=DataContext.ItemClicked}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
The component is used in MainWindow.xaml.
namespace WpfApp3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MyItemsControlViewModel _myViewModel;
public MyItemsControlViewModel MyViewModel { get { return _myViewModel; } }
public MainWindow()
{
_myViewModel = new MyItemsControlViewModel();
InitializeComponent();
myCustomControl.DataContext = MyViewModel;
}
}
}
XAML
<Window x:Class="WpfApp3.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:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:MyUserControl x:Name="myCustomControl"/>
</Grid>
</Window>
When i run the application i can see correctly the list of 3 items with the correct text.
But if i click on one of the button of the list i can't see the output of Debug.WriteLine("Executed!");
But if i click on the button btnOutsideItemsControl that is outside the ItemsControl, it works. I can see the output of Debug.WriteLine("Executed!");
So i think that also the definition of the command is correct.
To bind correctly the Command property of Button inside the ItemsControl i try this
<Button Command="{Binding ElementName=listItems, Path=DataContext.ItemClicked}">
And also this
<Button Command="{Binding DataContext.ItemClicked, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}}">
But it not works.
Please help!
You're gonna kick yourself once I tell you.
Your problem is that you set IsEnabled="False" on your ItemsControl. Remove it and all will be well with the universe.
I have problem to display another page inside MainWindow frame. When program starts it shows HomePage.xaml properly inside frame. It takes PagePath property from VM and binding works.When I try display another Page1.xaml in MainWindowViewModel, PagePath is changing, but PropertyChange event is always null and not invoking new property to frame source.
Code looks like this:
MainWindow.xaml
<Window x:Class="TEST.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:TEST.ViewModels"
mc:Ignorable="d"
Title="TEST" Height="768" Width="1024"
WindowState="Maximized">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Frame Source="{Binding PagePath}" NavigationUIVisibility="Hidden" />
</Grid>
</Window>
MainWindowViewModel.cs
namespace TEST.ViewModels
{
public class MainWindowViewModel : INotifyPropertyChanged
{
private static MainWindowViewModel _instance = new MainWindowViewModel();
public static MainWindowViewModel Instance { get { return _instance; } }
public event PropertyChangedEventHandler PropertyChanged;
private string pagePath = "/Pages/HomePage.xaml";
public string PagePath
{
get { return pagePath; }
set
{
pagePath = value;
OnPropertyChanged(nameof(PagePath));
}
}
protected void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
HomePage.xaml
<Page x:Class="TEST.Pages.HomePage"
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"
xmlns:local="clr-namespace:TEST.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="Home Page">
<Page.DataContext>
<local:HomePageViewModel />
</Page.DataContext>
<Grid>
<StackPanel>
<Button
Command="{Binding ShowPage1Command}"
Content="Show Page1"/>
</StackPanel>
</Grid>
</Page>
HomePageViewModel.cs
namespace TEST.ViewModels
{
public class HomePageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand ShowPage1Command { get; private set; }
public HomePageViewModel()
{
ShowPage1Command = new DelegateCommand((e) => ShowPage1());
}
protected void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void ShowPage1()
{
MainWindowViewModel.Instance.PagePath = "/Pages/Page1.xaml";
}
}
}
How to solve the problem or maybe it is wrong approach to navigate with frame?
Here is the method:
private void Capitales_SelectedChanged(object sender, RoutedEventArgs e)
{
string s = Capitales.SelectedItem.ToString();
tb.Text = "Selection: " + s;
}
I'm putting a list in the combobox, and when I compile the program, the textbox shows the next: ComboBox_MicroDocu.MainWindow+Ciudades, where "Ciudades" references my class.
You are writing WPF app as you whould do with Winform. This whould work, but there is a better way to do it. Use MVVM (Model View ViewModel).
MVVM is great since it allows you to decouple your views (xaml) from your business logic (viewModels). It's also great for testability.
Check out some good resources here https://www.tutorialspoint.com/mvvm/index.htm
this is how your code should looks like :
MainWindow.xaml
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" ItemsSource="{Binding Elements}" SelectedItem="{Binding SelectedElement}"/>
<TextBox Grid.Column="1" Text="{Binding SelectedElement}"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
private string _selectedElement;
public IEnumerable<string> Elements
{
get
{
for(int i = 0; i < 10; i++)
{
yield return $"Element_{i}";
}
}
}
public string SelectedElement
{
get
{
return _selectedElement;
}
set
{
_selectedElement = value;
RaisePropertyChanged();
}
}
private void RaisePropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I'm starting a WPF project. Trying to binding usercontrol by viewmodel. Where viewmodel define with dataType at DataTemplate in Application.Reousrces. But user control not bind. Any one can help me?
<Application.Resources>
<DataTemplate DataType="{x:Type vm:MatterPanelViewModel}">
<uc:MatterPanel />
</DataTemplate>
</Application.Resources>
Main Window where will bind user control.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:MyProject"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" x:Class="MyProject.MainWindow"
Title="MyProject" WindowState="Maximized" d:DataContext="{d:DesignInstance Type=uc:MainWindowViewModel}">
<Grid Grid.Row="2">
<ContentControl Content="{Binding CurrentViewModel}" Margin="10,0,10,10" />
</Grid>
</Window>
CurrentViewModel is the property of MainViewModel.
public class MainWindowViewModel:ViewModelBase
{
private ViewModelBase _currentViewModel;
public ViewModelBase CurrentViewModel
{
get { return this._currentViewModel; }
set
{
if(this._currentViewModel == value) { return; }
this._currentViewModel = value;
this.NotifyOfPropertyChange(() => this.CurrentViewModel);
}
}
public MatterPanelViewModel MatterPanelViewModel { get; set; }
public MainWindowViewModel()
{
this.MatterPanelViewModel = ServiceLocator.Current.GetService<MatterPanelViewModel>();
}
}
public class MatterPanelViewModel:ViewModelBase
{
public MatterPanelViewModel()
{
}
}
ViewModelBase here,
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged
{
add { this._propertyChanged += value; }
remove { this._propertyChanged -= value; }
}
private event PropertyChangedEventHandler _propertyChanged = delegate{ };
protected void NotifyOfPropertyChange<T>(Expression<Func<T>> property)
{
var lambda = (LambdaExpression)property;
MemberExpression memberExpression;
if(lambda.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)lambda.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
{
memberExpression = (MemberExpression)lambda.Body;
}
this.NotifyOfPropertyChange(memberExpression.Member.Name);
}
public void NotifyOfPropertyChange(string property)
{
this.RaisePropertyChanged(property, true);
}
private void RaisePropertyChanged(string property, bool verifyProperty)
{
var handler = this._propertyChanged;
if(handler != null)
{
handler(this, new PropertyChangedEventArgs(property));
}
}
}
Finally I solve the problem. The viewmodel and usercontrol map should under the MainWindow but here under main app. I just code from main app
<Application.Resources>
<DataTemplate DataType="{x:Type vm:MatterPanelViewModel}">
<uc:MatterPanel />
</DataTemplate>
</Application.Resources>
to Main Window
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<DataTemplate DataType="{x:Type vm:MatterPanelViewModel}">
<usc:MatterPanel/>
</DataTemplate>
</Window.Resources>
Then its working well.
I want to be able to bind a ListBox to a List<> or ObservableCollection<> and be able to change the List itself while keeping the binding.
In ViewModel:
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private ObservableCollection<string> items = new ObservableCollection<string>();
public ObservableCollection<string> Items
{
get
{
return items;
}
set
{
items = value;
RaisePropertyChangedEvent(nameof(Items));
}
}
private void FillItems()
{
Items1 = new ObservableCollection<string>();
Items.Add("1");
Items.Add("2");
}
in View:
<ListBox x:Name="listBox" ItemsSource="{Binding Items}"/>
Now when I call the FillItems() function, the list won't show items. But if I change the code like below it will work:
private void FillItems()
{
Items.Clear();
Items.Add("1");
Items.Add("2");
}
Insure that the view model implements INotifyPropertyChanged and notifies the view when ever the target property changes.
Using the following simple View
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ListBox x:Name="listBox" ItemsSource="{Binding Items}"/>
<Button Grid.Row="1" Content="Fill" Click="Button_Click" />
</Grid>
</Window>
Along with the following code behind and view model
namespace WpfApplication1 {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
private MainViewModel viewModel;
public MainWindow() {
InitializeComponent();
viewModel = new MainViewModel() {
Items = new List<string>() {
"a", "b"
}
};
this.DataContext = viewModel;
}
private void Button_Click(object sender, RoutedEventArgs e) {
viewModel.FillItems();
}
}
public class MainViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
Random random = new Random();
private List<string> items = new List<string>();
public List<string> Items {
get {
return items;
}
set {
items = value;
RaisePropertyChangedEvent(nameof(Items));
}
}
public void FillItems() {
var list = Enumerable.Range(0, 10).Select(i => random.Next(100).ToString()).Distinct().ToList();
Items = new List<string>(list);
}
}
}
Every time the button was clicked the ListBox was updated as expected for both List and ObservableCollection.
You should review your code again and make sure that the common practices that may be affecting your code are followed because what you provided in the example should work.