My model, viewmodel and XAML is as follows:
This is my ViewModelClass:
class AllResultsViewModel
{
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(param => this.MyAction(_cvm),
param => this._canExecute));
}
}
private bool _canExecute;
private ComboBoxViewModel _cvm;
public DataTable AllResults { get; set; }
public AllResultsViewModel(ComboBoxViewModel CVM)
{
_canExecute = true;
_cvm = CVM;
}
public void MyAction(ComboBoxViewModel cvm)
{
//Connecting to DB to retrieve data in datatable
}
}
public class CommandHandler : ICommand
{
private Action<object> _execute;
// private bool _canExecute;
private Predicate<object> _canExecute;
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public CommandHandler(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public CommandHandler(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
// return _canExecute;
return _canExecute == null ? true : _canExecute(parameters);
}
// public event EventHandler CanExecuteChanged;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
_execute(parameters);
}
}
My XAML is as follows:
<DataGrid Name="results_grid" IsReadOnly="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Margin="10" ItemsSource="{Binding AllResults}" DisplayMemberPath="AllResultsGrid" ColumnWidth="100" RowHeight="30">
My Model Class:
public class AllResultsModel
{
private DataTable _allresultsgrid;
public DataTable AllResultsGrid
{
get { return _allresultsgrid; }
set { _allresultsgrid = value; }
}
}
Am I missing anything here? The code is getting built successfully and the data is retrieved from DB. But I m not able to view it in Datagrid.
I looks like you are missing the propertychanged() call.
I am pretty sure that the data table does not fire any property changed events.
try calling propertychanged on the "AllResults" property once you finished loading the data to it.
Your code is very confused and I think you need to learn well how to use MVVM : https://www.tutorialspoint.com/mvvm/ (download the PDF).
In your Model.cs you need to define only your classes that defines your objects like below:
public class MyData
{
public int Par1{ get; set; }
public string Par2 { get; set; }
public string Par3 { get; set; }
}
Then you need to create an observable collection in your ViewModel implementing NotifiyPropertyChanged:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<MyData> myData = ObservableCollection<MyData>;
public ObservableCollection<MyData> MyData
{
get { return myData; }
set { myData = value; NotifyPropertyChanged("MyData"); }
}
}
Then in the ViewModel you will execute the MyAction() function like this:
public void MyAction(ComboBoxViewModel cvm)
{
//Connecting to DB to retrieve data in datatable
MyData = new ObservableCollection<MyData>(dataFromDB);
}
Finally you simply binding MyData in the ItemsSource in the xaml.
Remember to assign your viewmodel like page/windows datacontext!
I am a WPF application beginner and I'm currently using MvvmLight for my application.
I have a MainViewModel that holds a ObservableCollection of ChildViewModels(Type ViewModelBase). Each ChildViewModel is bound to a tab item in the XAML. So, I have a TabControl with tab items and each tab item has its own View and ViewModel.
This is my MainViewModel. For now, I have only one ChildViewModel which is DozentViewModel.
public class MainViewModel : ViewModelBase
{
private ObservableCollection<ViewModelBase> _vmCollection;
public ObservableCollection<ViewModelBase> VmCollection
{
get { return _vmCollection; }
set
{
Set(ref _vmCollection, value);
RaisePropertyChanged("VmCollection");
}
}
private DozentViewModel _dozentviewmodel;
public DozentViewModel Dozentviewmodel
{
get { return _dozentviewmodel; }
set
{
Set(ref _dozentviewmodel, value);
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(DozentViewModel dozentViewModel)
{
_dozentviewmodel = dozentViewModel;
VmCollection = new ObservableCollection<ViewModelBase>();
VmCollection.Add(_dozentviewmodel);
}
This is my ViewModelLocator code:
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<DozentViewModel>();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public DozentViewModel DozentVM
{
get
{
return ServiceLocator.Current.GetInstance<DozentViewModel>();
}
}
I am trying to implement Dependency Injection by using Constructor Injection in the MainViewModel. The above code works but I am not sure if my implementation of DI is right. I would like to have an interface of IChildViewModel with which all my ChildViewModels will be implemented. If I use an interface like so, how can I achieve DI?
Please find the code used for simple binding.
Xaml code:
Button Command="{Binding BoldCommand}"
C# code:
public partial class MainWindow : RibbonWindow
{
public BoldCommand BoldCommand
{
get;
set;
}
public MainWindow()
{
InitializeComponent();
BoldCommand = new BoldCommand();
DataContext = BoldCommand;
}
}
public class BoldCommand : ICommand
{
public BoldCommand()
{
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
}
}
The problem is that your BoldCommand in your DataContext does not have a BoldCommand property.
The major problem you are going to have is that you have conjoined your View and ViewModel. Replace your MainWindow code with the following and it should work.
public partial class MainWindow : RibbonWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
public class MainViewModel
{
public MainViewModel()
{
BoldCommand = new BoldCommand();
}
public BoldCommand BoldCommand { get; set; }
}
I created a WPF MVVM app using MEF and Prism 5.0. I have MEF loading a module at startup called MobileRecruiterModule (below). I need to read some settings from App.config (or any config file really) and update the ViewModel properties with those config values so the View can pick them up.
Where is the appropriate place to load settings here? Do I do it in the MEF module (the thing that implements Microsoft.Practices.Prism.Modularity.IModule or in my View?
MobileRecruiterModule.cs
[ModuleExport(typeof (MobileRecruiterModule))]
public class MobileRecruiterModule : IModule
{
/// <summary>
/// The region manager
/// </summary>
[Import] public IRegionManager Region;
/// <summary>
/// Notifies the module that it has be initialized.
/// </summary>
public void Initialize()
{
Region.RegisterViewWithRegion(RegionNames.MainContentRegion, typeof (MobileRecruiterView));
}
...
}
MobileRecruiterView.xaml.cs
[Export("MobileRecruiterView")]
[PartCreationPolicy(CreationPolicy.Shared)]
[RegionMemberLifetime(KeepAlive = false)]
[Export]
public partial class MobileRecruiterView : UserControl
{
[Import]
public MobileRecruiterViewModel ViewModel
{
get { return (MobileRecruiterViewModel)DataContext; }
set { DataContext = value; }
}
[ImportingConstructor]
public MobileRecruiterView(MobileRecruiterViewModel vm)
{
InitializeComponent();
DataContext = vm;
}
}
MobileRecruiterViewModel.cs
[Export]
public class MobileRecruiterViewModel : BindableBase
{
public string DatabaseServer { get; set; }
... and a few other properties that the XAML view binds to ...
}
I would suggest that you should load your settings in ViewModel constructor. Because your ViewModel is the DataContext for the View, you have to initialize it before you show it. I hope that you do not store any BLOB in it, so the time for *.config loading will be small enough to do it on UI thread.
I have a WPF 4.0 DataGrid that is bound to a DataTable using AutoGenerateColumns=True. The columns are dynamic, however I know there is always going to be a column named ID and I would like to hide this column. Is there a way I can do this?
in your datagrid, subscribe for the AutoGeneratingColumn event, the event args (DataGridAutoGeneratingColumnEventArgs) has the column name and a "Cancel", if the column name is ID then set Cancel = true. should do the trick.
You can use a behavior (reusable code) to do the job... This way you can use attribute which would centralize the column visibility in one place.
Usage:
<Window
...
xmlns:extension="clr-namespace:WpfControlLibrary.Extension;assembly=WpfControlLibrary">
<DataGrid ...
extension:DataGridBehavior.UseBrowsableAttributeOnColumn="True">
...
public class YourObjectItem
{
[Browsable(false)]
public Assembly Assembly { get; set; }
Code:
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace HQ.Wpf.Util.Behaviors
{
/// <summary>
/// Using this behavior on a dataGRid will ensure to display only columns with "Browsable Attributes"
/// </summary>
public static class DataGridBehavior
{
public static readonly DependencyProperty UseBrowsableAttributeOnColumnProperty =
DependencyProperty.RegisterAttached("UseBrowsableAttributeOnColumn",
typeof(bool),
typeof(DataGridBehavior),
new UIPropertyMetadata(false, UseBrowsableAttributeOnColumnChanged));
public static bool GetUseBrowsableAttributeOnColumn(DependencyObject obj)
{
return (bool)obj.GetValue(UseBrowsableAttributeOnColumnProperty);
}
public static void SetUseBrowsableAttributeOnColumn(DependencyObject obj, bool val)
{
obj.SetValue(UseBrowsableAttributeOnColumnProperty, val);
}
private static void UseBrowsableAttributeOnColumnChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var dataGrid = obj as DataGrid;
if (dataGrid != null)
{
if ((bool)e.NewValue)
{
dataGrid.AutoGeneratingColumn += DataGridOnAutoGeneratingColumn;
}
else
{
dataGrid.AutoGeneratingColumn -= DataGridOnAutoGeneratingColumn;
}
}
}
private static void DataGridOnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var propDesc = e.PropertyDescriptor as PropertyDescriptor;
if (propDesc != null)
{
foreach (Attribute att in propDesc.Attributes)
{
var browsableAttribute = att as BrowsableAttribute;
if (browsableAttribute != null)
{
if (!browsableAttribute.Browsable)
{
e.Cancel = true;
}
}
// As proposed by "dba" stackoverflow user on webpage:
// https://stackoverflow.com/questions/4000132/is-there-a-way-to-hide-a-specific-column-in-a-datagrid-when-autogeneratecolumns
// I added few next lines:
var displayName = att as DisplayNameAttribute;
if (displayName != null)
{
e.Column.Header = displayName.DisplayName;
}
}
}
}
}
}
Other possibility would be Visibility.Collapsed:
private void dataGrid_AutoGeneratingColumn(object sender,
DataGridAutoGeneratingColumnEventArgs e)
{
//Set properties on the columns during auto-generation
switch (e.Column.Header.ToString())
{
case "rownameYouWantToHide":
e.Column.Visibility = Visibility.Collapsed;
break;
}
}
I wouldn't say it's great solution... but... you can have one more abstraction layer
for example let's say you have an object like:
public class Foo
{
public string Id { get; set; }
public string Property2 { get; set; }
public string Property3 { set; get; }
}
You don't want column for Id, so you create new object
public class Foo2
{
public string Property2 { get; set; }
public string Property3 { set; get; }
}
then map/convert Foo to Foo2 and you are done.
Another possible way (not always possible) is to change access modifier to internal
public class Foo
{
internal string Id { get; set; }
public string Property2 { get; set; }
public string Property3 { set; get; }
}
this way you won't have Id column generated either.
I achieved this using Browsable attribute and Visibility: Collapsed
Model
class CourseModel
{
[Description("")]
[ReadOnly(false)]
public bool Select { get; set; } = true; // Checkbox
[Description("Course ID")]
[ReadOnly(true)]
[Browsable(false)]
public string ID { get; set; } // Hidden column
[Description("Course Title")]
[ReadOnly(true)]
public string Title { get; set; }
}
Custom control extension:
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace MyProject.FrontEnd.Controls
{
class CustomDataGrid : DataGrid
{
// Take attributes of POCO, if available (https://stackoverflow.com/a/17255496/979621)
protected override void OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventArgs e)
{
try
{
base.OnAutoGeneratingColumn(e);
var propertyDescriptor = e.PropertyDescriptor as PropertyDescriptor;
e.Column.Header = propertyDescriptor.Description;
e.Column.IsReadOnly = propertyDescriptor.IsReadOnly;
e.Column.Visibility = propertyDescriptor.IsBrowsable
? Visibility.Visible
: Visibility.Collapsed;
}
catch
{
// ignore; retain field defaults
}
}
}
}
ViewModel
public ObservableCollection<CourseModel> Courses { get; set; }
XAML
<Window
...
xmlns:controls="clr-namespace:MyProject.FrontEnd.Controls"
...
>
...
<controls:CustomDataGrid x:Name="courses"
ItemsSource="{Binding Path=Courses, Mode=TwoWay,
NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}" />
I can't speak for 4, however it was not possible in 3.5 SP1, at least without registering for an event which I wanted to avoid at all costs.
What you could do instead is change your generation code to AutoGenerateColumns=False then just place the columns you care about within the XAML as the underlying data will all still be placed within the columns appropriately
<dg:DataGridTextColumn Header="Display" Binding="{Binding DisplayName}"/>
<dg:DataGridTextColumn Header="Host" Binding="{Binding HostName}"/>
<dg:DataGridTextColumn Header="Database" Binding="{Binding Database}"/>
<dg:DataGridTextColumn Header="Username" Binding="{Binding Username}"/>
<dg:DataGridTextColumn Header="Password" Binding="{Binding Password}"/>
This will allow you to display the only columns you care about in relation to the underlying model as well as change the Header to display as you see fit, so you are not tied to the Property name on the model.
I've recently done this and wanted to share my solution:
I just made a view model I wanted the datagrid to follow and for the fields I wanted it to ignore (that is, not auto generate columns for), simply set those fields to private. Worked like a charm and there's no need for unnecessary code.
For example, here's the view model I pass to the view that contains the datagrid. I get all I need by simply setting the fields I don't want as columns to private, shown on the "FullPath" field in my example. I understand this may not be possible in every scenario, but worked for me quite well.
namespace dev
{
/// <summary>
/// View model for tag list view in data grid
/// </summary>
public class TagDataViewModel : BaseViewModel
{
/// <summary>
/// Default constructor
/// </summary>
/// <param name="path">The JSONPath to this item</param>
public TagDataViewModel(string path)
{
FullPath = path;
}
/// <summary>
/// Gets and sets the JSONPath to this item
/// </summary>
private string FullPath { get; set; }
/// <summary>
/// Gets the name
/// </summary>
public string Name => ProjectHelpers.GetPropertyValue(FullPath, "Name");
/// <summary>
/// Gets the address
/// </summary>
public string Address => ProjectHelpers.GetPropertyValue(FullPath, "Address");
/// <summary>
/// Gets the data type
/// </summary>
public string DataType => ProjectHelpers.GetPropertyValue(FullPath, "DataType");
/// <summary>
/// Gets the scan rate
/// </summary>
public string ScanRate => ProjectHelpers.GetPropertyValue(FullPath, "ScanRate");
/// <summary>
/// Gets the scaling type
/// </summary>
public string ScalingType => ProjectHelpers.GetPropertyValue(FullPath, "ScalingType");
}
}