views:

312

answers:

3

Seriously, it seems like every time I want to make my UI elements talk to each other, I end up coding a new, custom, IValueConverter :(. Someone tell me that I'm doing it wrong, please!

Examples:

  • I wanted a button to be enabled only if my textbox contained a valid URI. Great, time to code up a UriIsValidConverter!
  • Oh oops, I also wanted to disable it while I'm processing something. I guess now I need to code up a UriIsValidAndBoolIsFalseMultiConverter!
  • I want to display a list of files in a certain directory (specified by a textbox) inside a listbox. I guess I need a DirectoryPathToFileList converter!
  • Oh hey, I want icons for each of those files in the listview. Time for a FileInfoToBitmap converter!
  • I want my status to be red if my status-string contains "Error", and green otherwise. Yay, I get to code up a StatusStringToSolidColorBrushConverter!

I'm really thinking this isn't that much better than the old Windows Forms method of just wiring up everything manually using TextChanged events (or whatever). Which I guess is still an option. Is that what people actually do, perhaps, and I'm trying too hard to make everything fit into the databinding paradigm?

So yeah, please tell me if this is really how WPF coding is---or if I'm doing it wrong, and if so, what I should be doing.

+9  A: 

Your approach is perfectly valid (though I would use a multibinding for the second example, rather than a such a specialised converter), though by placing all your logic into the XAML you are producing very high coupling between the way the application looks and the way that it behaves, because of this you may want to look into the MVVM pattern to separate those things out.

Under the MVVM pattern your XAML (the view) just contains very simple data bindings into a ViewModel which handles all the logic and updates the view through the INotifyPropertyChanged interface. The code for your third example may look something like:

public class DirectoryManagerViewModel : INotifyPropertyChanged
{
    private string _directory;

    public string Directory
    {
        get { reutrn _directory; }
        set
        {
            if (_directory != value)
            {
                 _directory = value;
                 OnPropertyChanged("Directory");
                 if (IsValidDirectory(value))
                 {
                     PopulateFiles();
                 }
            }
        }
    }

    public ObservableCollection<FileViewModel> Files { get; private set; }

    private bool IsValidDirectory(string directory)
    {
          //return if the directory exists etc.
    }

    private bool PopulateFiles()
    {
         //Populate Files with files in directory
    }
}

Where FileViewModel is another view model which contains the name and the icon for a file.

The advantage of this approach is that the ViewModels can be reused with other views and other technologies such as ASP.NET or Winforms so you are not locked into the WPF stack. (alos if you work in an environment where there are designers responsible for the look and developers responsible for the behaviour, this helps define those boundaries)

At the end of the day though this logic does need to go somewhere and while there are better and worse ways to architect your application you are still going to be writing code that takes a string and converts it into a series of filenames and icons somewhere.

Martin Harris
MVVM is the answer to this guy's ails. Logic for validation and turning this on and that off all goes in the ViewModel.
Will
Thank you, and the other answerers too! I looked at MVVM when starting out, and, well, got scared by the complexity. But your example was really helpful in seeing just the basics of it, and convincing me to give it another go. Your point about the code having to go somewhere is also well-taken.
Domenic
+4  A: 

First, you might want to start by reading about the Model-View-ViewModel pattern (MVVM). Josh Smith had a fantastic article in MSDN Magazine recently. MVVM and WPF go perfectly together. Done right, you won't need IValueConverters so much. The way that you are going about it now is causing a very tight coupling between your visualization and your application actions. MVVM is designed to decouple these elements.

In this context, your view model will track state for you. Your button will be enabled if the CanExecute method on a certain ICommand in your view model returns true. This same concept can handle disabling the button when processing something.

You want to display a list of files in a certain directory that is specified inside a listbox? Have a DirectoryViewModel view model that will handle providing the list of files to the view by binding to the view model. The display of the files can be specified with a DataTemplate specified in XAML with no code behind. This same concept can handle providing the icons to the view whose display can be specified in the template.

You want your status to be red if a status message contains "Error" and green otherwise? Let a view model handle determining the state and let the view bind to that state and now you only need an IStateConverter to convert the state to red or green appropriately (this is one of many ways to handle this problem in the MVVM context).

Get in the habit of keep data and state separate from your view and your applications will be loosely coupled, easier to design and maintain, and easier to test.

Jason
+4  A: 

Don't know if you are wrong, just making it a lot harder than it has to be!

I use MVVM, so where you are writing customer converters, I provide a bindable property on the view model that tells the view what to do. For example:

  1. To display a list of files, I provide a collection that contains that list.
  2. If I want icons the object in that collection has a icon property
  3. If I want a status to be red or green I provide a StatusColorbrush property.

By moving this logic into the view model, I get:

  1. much simpler Xaml.
  2. can test my view logic without the view.

This approach uses one of the strong points of WPF, it's binding capabilities.

automatic