简体   繁体   中英

How to achieve dynamic binding in WPF/MVVC C#

I am rather new to MVVC/wpf, having mostly worked with winforms.

What I want to accomplish is dynamic databinding without using code behind in WPF. The user interface consists of a devexpress grid and a couple of buttons. Each button press loads an object list and presents the objects in the grid. The lists contain different object types depending on the button pressed. For this example I have two classes to present: FatCat and FatDog.

In winforms this works:

    private void button1_Click(object sender, EventArgs e)
    {
        ((GridView)gridCtrl.MainView).Columns.Clear();
        gridCtrl.DataSource = new BindingSource(itsModel.GetAll<FatDog>(), null);
    }


    private void button2_Click(object sender, EventArgs e)
    {
        ((GridView)gridCtrl.MainView).Columns.Clear();
        gridCtrl.DataSource = new BindingSource(itsModel.GetAll<FatCat>(), null);
    }

I have configured the grid to create columns dynamically, so everything just works. itsModel is of type CatClientModel.

In wpf I have defined the DataContext to be CatClientModel.

What should I use for ItemsSource in the grid to achieve the same behaviour as my winforms solution?

dxg:GridControl ItemsSource="{Binding SomeDynamicList}"

In other words, what should SomeDynamicList be in the code above? Or am I going about this the wrong way?

I am, as I stated, using the DevExpress wpf grid control, but the question ought to be general and apply to any control presenting object lists.

In other words, what should SomeDynamicList be in the code above?

SomeDynamicList should be an ObservableCollection<T> property to which you can add any objects of type T that you want to display in the GridControl .

Set the DataContext of the GridControl , or any of its parent elements, to an instance of a class where this property is defined:

public class CatClientModel
{
    public ObservableCollection<Animal> SomeDynamicList { get; } = new ObservableCollection<Animal>();
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new CatClientModel();
    }
}

Ok. But the thing is that the ObservableCollection contains different types. Unfortunately there is no feasible class to inherit from. I want to bind to either ObservableCollection or ObservableCollection depending on which button was pressed

Switch the DataContext then, or change the property into an IEnumerable and set it to a new collection each time the button is clicked. This requires you to implement the INotifyPropertyChanged interface in your view model

private System.Collections.IEnumerable _collection;
public System.Collections.IEnumerable MyProperty
{
    get { return _collection; }
    set { _collection = value; OnPropertyChanged(); }
}

If you want to use XAML to define which data sources your code maps to for each grid that is possible. That does require at least some method of MVVM manager either prism or mvvmlight to connect the view model to the view.

so if you do go the MVVM model route, the Model would contain a description for each of your grids like this:

 public BulkObservableCollection<icd10facet> FacetList
    {
        get { return this._facets; }
        set { SetProperty(ref this._facets, value); }

    }
    public INotifyTaskCompletion<BulkObservableCollection<PetsConvert>> ConceptList
    {
        get { return this._concept; }
        set
        {
            SetProperty(ref this._concept, value);
        }
    }

In the XAML for your code the grid woud bind to the grid defined by ConceptList in this way:

ItemsSource="{Binding ConceptList.Result}" 

this answer does NOT address how to wire up Prism 6.0 for example to use a view model but for examples see:

https://github.com/PrismLibrary/Prism

Which contains documentation and starter code. Keep in mind that there is not any specific reason that putting code in the code behind for the view is a problem, first solve the problem and then refactor if separation of concerns is an issue for you.

Using this technique you can bind each grid to its own data source. In the MVVM space buttons and other things use a commanding model to communicate with the view model.

 <Button Content="Load Rule Data" Width="100" Height="40" HorizontalAlignment="Left" Margin="5px" Command="{Binding LoadRuleData }"/>

this requires defining a command delegate in the viewmodel for LoadRuleData

    public DelegateCommand LoadRuleData { get; private set; }

and then (usually in the constructor) wire the DelegateCommand to the method that is going to do the work.

 this.LoadRuleData = new DelegateCommand(this.loadRules);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM