2

Is it possible to create a custom control with dependencies properties respecting the MVVM WPF pattern?

If yes, how do you use the CustomControl in another MVVM application and expose the dependencies properties ?

EDIT:

Below a simple example that allows me to create a customControl then I use it in an another WPF App named "TestCustomControl". But, the dependency Property doesn't work at all for me.

enter image description here

CustomControlView.xaml

<UserControl xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"  xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"  x:Class="MyCustomControl.MyCustomUserControl"
         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:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
         xmlns:myCustomControl="clr-namespace:MyCustomControl"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">

<dxmvvm:Interaction.Triggers>
    <dxmvvm:EventToCommand Command="{Binding LoadCommand}" EventName="Loaded" />
</dxmvvm:Interaction.Triggers>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <dxe:ButtonEdit Height="40" Grid.Row="0"/>
    <dxg:GridControl Grid.Row="1" ItemsSource="{Binding MyItems}" AutoGenerateColumns="AddNew"/>
</Grid>

CustomControlView.xaml.cs

using System.Windows;
using System.Windows.Controls;
namespace MyCustomControl
{
    /// <summary>
    /// Interaction logic for MyCustomUserControl.xaml
    /// </summary>
    public partial class MyCustomUserControl : UserControl
    {
        public MyCustomUserControl()
        {
            InitializeComponent();
            this.DataContext = new CustomControlViewModel(FilePath);
        }
        /// <summary>
        /// File Path 
        /// </summary>
        public static readonly DependencyProperty FilePathProperty = DependencyProperty.Register(
            "FilePath", typeof(string), typeof(MyCustomUserControl), new PropertyMetadata(string.Empty));
        public string FilePath
        {
            get { return (string)GetValue(FilePathProperty); }
            set
            {
                SetValue(FilePathProperty, value);
            }
        }
    }
}

CustomControlViewModel.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using DevExpress.Mvvm;
using DevExpress.Mvvm.DataAnnotations;
namespace MyCustomControl
{
    public class CustomControlViewModel:ViewModelBase
    {
        #region Fields
        private ObservableCollection<string> _myItems;
        private string _path;
        #endregion

        #region Constructors
        public CustomControlViewModel(string path)
        {
            _path = path;
        }
        #endregion

        #region Commands

        [Command]
        public void Load()
        {
            IEnumerable<string> allLinesText = new List<string>();
            try
            {
                allLinesText = File.ReadAllLines(_path).ToList();
            }
            catch (Exception e)
            {

                Console.WriteLine(e.ToString());
            }

            MyItems = new ObservableCollection<string>(allLinesText);
        }
        #endregion

        #region Properties
        public ObservableCollection<string> MyItems
        {
            get { return _myItems; }
            set { SetProperty(ref _myItems, value, () => MyItems); }
        }
        #endregion
    }
}

MainWindow.xaml

<Window xmlns:MyCustomControl="clr-namespace:MyCustomControl;assembly=MyCustomControl"  
    x:Class="TestCustomControl.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:testCustomControl="clr-namespace:TestCustomControl"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <testCustomControl:MainViewModel/>
</Window.DataContext>
<Grid>
    <MyCustomControl:MyCustomUserControl FilePath="{Binding MyFile}"/>
</Grid>

MainViewModel.cs

using DevExpress.Mvvm;
namespace TestCustomControl
{
    public class MainViewModel: ViewModelBase
    {
        #region Fields
        private string _myFile;        
        #endregion

        #region Constructors
        public MainViewModel()
        {
            MyFile = "List.txt";
        }
        #endregion

        #region Properties
        public string MyFile
        {
            get { return _myFile; }
            set { SetProperty(ref _myFile, value, () => MyFile); }
        }
        #endregion
    }
}

NB: "List.txt" is a file placed into "..\TestCustomControl\bin\Debug"

Can Someone help me to find why my dependency property doesn't work ?

xtensa1408
  • 584
  • 12
  • 32
  • A UserControl or custom Control would always expose dependency properties to support data binding. This is however entirely unrelated to the architecture of the application where you're using it. Whether it's MVVM or not doesn't matter for the UserControl code. On thing to keep in mind is that a typical UserControl should not define its own view model, especially it should not explicitly set its own DataContext. Instead, the DataContext should be inherited from its parent control or window. – Clemens Jul 07 '16 at 17:11
  • 1
    Reusable controls are solely UI and should not come with their own view models. Like how the TextBox doesn't have a TextBoxViewModel. Simply expose what you need as DependencyProperties and put your code in the codebehind and you can use that control in any type of WPF application. –  Jul 07 '16 at 18:10
  • 1
    @Will That seems like good advice for something simple like a `Textbox` that only ever binds to a `string`. But what about user controls for editing something complicated like a `User` class that would have many complex and nested properties? Surely the user control for a `User` would get bound to an instance of `User`. wouldn't that `User` be considered the View Model for the control? In some ways it feels to me like `string` IS the View Model for a `Textbox`. – Bradley Uffner Jul 07 '16 at 18:36
  • 3
    I tend to use UserControls in one of two ways when using MVVM : Either as a more elaborate DataTemplate, with the UserControl assuming it's `DataContext` will always be of a specific type, or as a stand-alone control, such as a `CalendarControl` or `PopupControl`, that uses Dependency Properties to take in any input it needs for display. See [here](http://stackoverflow.com/a/25673948/302677) if you want an example. – Rachel Jul 07 '16 at 18:51
  • 1
    @BradleyUffner I do that all the time, but the VM isn't designed for the UC, the UC is designed for the VM, and expects the DataContext to be of that type. People screw themselves when they replace the DataContext of the UC with the UC's view model. The distinction is slight, but very important. The MVVM tag is full of people who have binding issues because they are putting UI logic in a VM designed for the UC, and then have to interact with another VM. –  Jul 07 '16 at 19:27

1 Answers1

1

It's obviously possible and it's the best way to create a custom control. Because without dependency properties we can't easily reuse a custom control. Re-usability becomes very much easier with dependency properties. You could event use ICommand as Dependency properties thereby following MVVM pattern and having a much cleaner code behind.

If I elaborate on how to reuse a CustomControl in another MVVM application it would be too broad to be answered here. YOu could just go to Visual studio and create a custom control. In the code behind define a few dependency properties which are bound to properties which you think are dynamic, in the view. Reuse this very customcontrol in another application and set these properties while reusing it.

You could also try out with an ICommand for routing some event to the view model. ie the Dependency Property for an List item selection changed event could route a command to the corresponding view model.

ViVi
  • 4,339
  • 8
  • 29
  • 52
  • 1
    I know that it's possible to create custom control. But the question was is it possible to create it with MVVM arch. Because i already created a custom control with dependencies properties in code behind and i consumed it in another wpf application, too. However when i tried to create custom control with MVVM I coudn't consume dependencies properties !!! – xtensa1408 Jul 07 '16 at 18:01
  • Who told you cant consume dependecy properties in MVVM? It's obviously possible. Say you have a custom control derived from a button. You want to use MVVM in it. Define a dependency property in code behind with type **ICommand** say **ButtonDoubleClicked**. Raise this command on the double click handler of the button. Now when you are reusing this custom control, define the **ButtonDoubleClicked** to some ICommand property in the corresponding view model and it will be invoked. This is just a short example. You could get lots of examples in google. – ViVi Jul 08 '16 at 03:27
  • @xtensa1408: Reusable controls don't have a view model and don't require one. You could create a custom control with just code behind. You could then reuse it in a view which is bound to a view model. Hope you got the point. – ViVi Jul 08 '16 at 17:47
  • 1- That's mean that my code above is right and I have just to remove VM and do all the work of my Custom Control into code behind. 2- you mean also that it's impossible to create CustomControl with VM ! So what if I have a sophisticated CustomControl and all member of team will work on it, and I want share the work between View and ViewModel !! – xtensa1408 Jul 08 '16 at 17:53
  • 1
    You still did not get the point it seems. Which user control will get that much sophisticated? I have worked in many WPF projects. If reusability is your sole requirement you need to do it in code behind. Because it is just the view. That is why dependency properties are available. Just make use of it. If you define a ViewModel for a custom control, then you write the code in the viewmodel. Then where comes reusability? Just use dependency properties in code behind for your requirement. – ViVi Jul 08 '16 at 17:57
  • Happy to know that. Feel free to ask if you need any more clarifications. Keep coding. Peace out! – ViVi Jul 08 '16 at 18:14