There were only 2 of us on the project for the 1st 8 months (and my colleague 
spent a lot of that time on the WCF end of things), so I do appreciate how 
difficult it ends up being.  My rule of thumb has always been to write what you 
need when you need it, when you find yourself copying it for the 2nd or 3rd  
time to use elsewhere, it's time to make yourself a baseclass and derive from 
then on.  Trying to second guess when it's really worth writing base classes & 
what they may need to do in the future will drive you insane and lead to 
endless refactoring unless you're amazingly prescient.

One thing which we do a little differently than most is to use 
ObjectDataProviders as our data sources rather than binding directly through an 
assigned DataContext (most of the time).   What I found was confusing early on 
with complex data sources, was telling from looking at the XAML exactly what on 
earth any control was binding to exactly.

So when our IOC creates the View and ViewModel classes, it passes the VM 
instance into the View via a method a bit like this :

      /// <summary>
      /// The view model associated with this view.
      /// </summary>
      public AccountWalletVM ViewModel
      {
         get
         {
            return _viewModel;
         }
         set
         {
            _viewModel = value;
            ModelObjectDataProvider modp = 
((ModelObjectDataProvider)this.FindResource("ViewModel"));
            modp.ObjectInstance = _viewModel;
            modp.Refresh();
         }
      }
      private AccountWalletVM _viewModel = null;

Then in the XAML <UserControl.Resources> section :

<ss:ModelObjectDataProvider x:Key="ViewModel"
                                  d:IsDataSource="True"
                                  ObjectType="{x:Type 
ViewModels:AccountWalletVM}" />

The ModelObjectDataProvider is just a class which derives from the standard 
ObjectDataProvider, but does some deferring of the bindings until the data's 
actually there.  I've included the class definition at the bottom of the email 
for reference (it's rather fat with comments, but simple in concept).

I can't entirely remember all the discussion and approaches we tried when 
settling on this standard, but now most of our XAML binding looks like this 
below.  I remember one of the issues we had early on with Binding was that we 
ended up with grid/itemscollections where we wanted some data from 1 data 
source & then something like the Button Command or choices for a ComboBox to 
come from another object..  Our early VMs got very complex because of a belief 
we could only really bind to one thing.  Being explicit about the main VM to 
which the View is bound, allows you to leap out at any time to look at 
something exposed from that VM, regardless of how many objects down a 
collection/hierarchy of properties you are at that time...  It adds some extra 
boilerplate which goes into a lot of your bindings (though you can cheat and 
stick the DataContext on a GroupBox to avoid the need to add the source for all 
your child controls then), but saves much complex walking of Ancestors to find 
the data source you want.


   <DataGrid x:Name="dgWallets"
                      ItemsSource="{Binding Source={StaticResource ViewModel}, 
Path=Wallets, Mode=OneWay}">
               <DataGrid.Columns>
                  <DataGridTextColumn Header="{Binding Source={StaticResource 
ViewModel}, Path=Strings.V_ListHeadingWalletName, Mode=OneTime}"
                                                                
Binding="{Binding Path=Name}" />
                   <DataGridTemplateColumn>
                           <DataGridTemplateColumn.CellTemplate>
                              <DataTemplate>
                                 <Button Style="{DynamicResource 
DeleteButtonStyle}"
                                                   Command="{Binding 
Source={StaticResource ViewModel}, Path=DeleteWalletCommand}"
                                                   CommandParameter="{Binding}" 
/>
                              </DataTemplate>
                           </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
               </DataGrid.Columns>
            </DataGrid>

No idea if any of this is of interest to anyone, but figured I'd throw it out 
there incase it helps someone get started.

Martin

  /// <summary>
   /// A custom ObjectDataProvider which overrides the ObjectType property such 
that it can be
   /// used at designtime to link an appropriate dummy data object, while 
ignoring that XAML binding
   /// value at runtime so that ObjectInstance can be used to set the data to 
which the data provider
   /// refers.
   /// </summary>
   /// <remarks>
   /// This is necessary because the two ways of setting the content of an 
ObjectDataProvider, ObjectType and
   /// ObjectInstance are mutually exclusive once they've been called.  The 
ObjectType property is much more
   /// convenient for gaining a design-time binding for design purposes, but 
the ObjectInstance is more
   /// suitable architecturally when the ViewModel is created or populated 
elsewhere (or shared between views)
   /// </remarks>
   public class ModelObjectDataProvider : ObjectDataProvider
   {
      bool isInDesignMode = 
(bool)DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue;

      private IDisposable m_Defer;

      public ModelObjectDataProvider()
      {
         // The class is not interested in the XAML declaration!<br/>           
 // IsInitialLoadEnabled is setted to true by the base (DataSourceProvider) 
constructor.
         // if you listen to the PropertyChanged event, you will see,
         // that IsInitialLoadEnabled is setted to the XAML value after 
BeginQuery has been called
         IsInitialLoadEnabled = false;
      }

      protected override void BeginInit()
      {
         // increment DataSourceProvider._deferLevel
         if (!IsInitialLoadEnabled)
         {
            m_Defer = this.DeferRefresh();
         }
         base.BeginInit();
      }

      protected override void EndInit()
      {
         // decrement DataSourceProvider._deferLevel
         if (m_Defer != null)
         {
            m_Defer.Dispose();
            m_Defer = null;
         }
         else
         {
            base.EndInit();
         }
      }



      /// <summary>
      /// Gets or sets the type of object to create an instance of.
      /// </summary>
      /// <value></value>
      /// <returns>
      /// This property is null when the <see 
cref="T:System.Windows.Data.ObjectDataProvider"/> is uninitialized or 
explicitly set to null. If <see 
cref="P:System.Windows.Data.ObjectDataProvider.ObjectInstance"/> is assigned, 
<see cref="P:System.Windows.Data.ObjectDataProvider.ObjectType"/> returns the 
type of the object or null if the object is null. The default value is null.
      /// </returns>
      /// <exception cref="T:System.InvalidOperationException">
      ///     <see cref="T:System.Windows.Data.ObjectDataProvider"/> is 
assigned both an <see 
cref="P:System.Windows.Data.ObjectDataProvider.ObjectType"/> and an <see 
cref="P:System.Windows.Data.ObjectDataProvider.ObjectInstance"/>; only one is 
allowed.
      /// </exception>
      public new Type ObjectType
      {
         get { return base.ObjectType; }
         set
         {
            if (!isInDesignMode)
            {
               value = null;
            }

            base.ObjectType = value;

            if (isInDesignMode)
            {
               this.Refresh();
            }

         }
      }


From: [email protected] [mailto:[email protected]] On 
Behalf Of Greg Keogh
Sent: 25 November 2011 22:26
To: 'ozWPF'
Subject: RE: Getting up to speed in wpf

Yep, Thanks Martin for your time on that. You have confirmed a lot of my 
experiences, and perhaps that I'm not mad. So far I have written all my WPF 
alone without the help of a team to build comprehensive helper libraries of 
base classes and utilities. I certainly do find myself copying and pasting 
snippets out of other apps a lot. I also try to separate all the styling XAML 
from the controls. I have been very unhappy with MVVM as it seems to stretch 
the capabilities of binding beyond the breaking point as an academic exercise 
and leads to bizarre quirks and workarounds. I have settled on a simple 
View-Controller pattern which keeps the code behind quite thin and allows unit 
testing of the controllers.

I am back to coding WPF today and I will be editing more XAML by hand.

Greg

Clarity Retail Systems Limited, Paterson House, Hatch Warren Farm, Hatch Warren 
Lane, Hatch Warren, Basingstoke,
Hants, RG22 4RA, UK - Company Registration No: 02739937 - Registered in England

The contents of this email are intended for the named addressee(s) only. It 
contains information which may be confidential and which may also be 
privileged. If you are not the intended recipient(s) please note that any form 
of disclosure, distribution, copying or use of this communication or the 
information in it or in any attachments is strictly prohibited and may be 
unlawful. If you have received it in error please notify the sender immediately 
by return email or by calling +44(0)1256 365150 and ask for the sender. Where 
the content of this email is personal or otherwise unconnected with Clarity 
Retail Systems Limited or our clients' business, Clarity Retail Systems Limited 
accepts no responsibility or liability for such content. It is your 
responsibility to verify that this email and any attachments are free of 
viruses, as we can take no responsibility for any computer viruses.
_______________________________________________
ozwpf mailing list
[email protected]
http://prdlxvm0001.codify.net/mailman/listinfo/ozwpf

Reply via email to