This project is read-only.

Confused about u:Update and ForView.Wrap

Sep 4, 2009 at 6:30 PM

In some of your demos you use u:Update and in other you just use Binding.

Also, in some demos you use

DataContext = ForView.Wrap(new PresentationThing(Thing));

and in others you use

DataContext = new PresentationThing(Thing);

Which do I want to use when and why?

Thanks!

- Brian

Sep 4, 2009 at 7:46 PM

The custom markup extension "u:Update" was the old way. It has been deprecated in favor of ForView.Wrap, which supports the standard markup extension "Binding".

To summarize, do not use:

  • u.Update
  • DataContext = new PresentationThing(Thing);

Do use:

  • Binding
  • DataContext = ForView.Wrap(newPresentationThing(Thing));
Sep 21, 2009 at 6:36 PM
Edited Sep 21, 2009 at 6:51 PM

Ok thanks! That worked out well.

One other thing... I'm trying to use this with the items in a TreeView and HierarchicalDataTemplate.Triggers.

I'm a little new to WPF and I was having a problem getting this to work with the TreeView... but, I thought I would ask if UpdateControls generally should work with this before I try to dig deeper.

- Brian

PS:  a TreeView sample would be wonderfull! :)

I'm trying to change the image when an item gets expanded and of course adding and removing things from the TreeView.

 

Sep 23, 2009 at 8:57 PM

Yes, Update Controls works well with TreeView. In fact, I'm finishing up a project right now that would make for great sample fodder. It includes a TreeView, among other interesting tidbits. I'll anonymize it and get it posted soon.

I haven't done the expanded image trigger, yet, but it seems that it should work. There is no interaction with Update Controls in this case, since the Trigger would be based on a property of the TreeViewItem, not of the view model.

Sep 23, 2009 at 9:07 PM

I'm using the expanded event to set a property in my view model. (and that property is tied back so I can programatically expand/unexpand a node).

Can't wait for that sample! :)

Thanks!

Oct 12, 2009 at 3:24 AM

The TreeViewItem sample has been posted here:

http://updatecontrols.net/doc/examples/treeview/non_recursive

Hope this helps. Enjoy!

Feb 10, 2010 at 12:22 AM
MichaelLPerry1971 wrote:

The custom markup extension "u:Update" was the old way. It has been deprecated in favor of ForView.Wrap, which supports the standard markup extension "Binding".

To summarize, do not use:

  • u.Update
  • DataContext = new PresentationThing(Thing);

---------------

Michael,

I am using your suggested Navigation pattern.

Well, the application is using more than one ViewModel. There is MainViewModel which in turn uses some other viewmodel and so forth.

The ForView.Wrap is located only at App.xaml. There is no code behind whatsoever in any of the views, everything is databind.

Now when I bind IsEnabled to one of the navigation models properties like so

IsEnabled="{Binding ClientNavigation.IsClientSelected}"

It works only once during loading of the view.

When I bind using old method

IsEnabled="{u:Update ClientNavigation.IsClientSelected}"

It works always. Now all other bindings work correctly and get updated correctly as well, the only problem is with IsEnabled.

Do you have any ideas why this is happening?

You may look below at more complete version of XAML. Notice other bindings using UpdateControls, they work great, except IsEnabled on StackPanel.

                <wpfToolkit:DataGrid 
                    Grid.Row="0"
                    ItemsSource="{Binding Clients}"
                    SelectedItem="{Binding ClientNavigation.CurrentClient, Mode=TwoWay}"
                    AutoGenerateColumns="False" 
                    Name="dataGrid1" 
                    VerticalAlignment="Top"
                    HorizontalAlignment="Stretch"
                    CanUserAddRows="False"
                    CanUserDeleteRows="False"
                    Background="Transparent"
                    Foreground="White"
                    RowBackground="Transparent"
                    >
                    <wpfToolkit:DataGrid.Columns>
                        <wpfToolkit:DataGridTextColumn Header=" Client Name " Width="Auto"  
                                               IsReadOnly="True"
                                               Binding="{Binding Path=ClientName, Mode=OneWay}"
                                               />
                        <wpfToolkit:DataGridTextColumn Header=" Client Code " Width="Auto"  
                                               IsReadOnly="True"
                                               Binding="{Binding Path=ClientCode, Mode=OneWay}"
                                                   />
                        <wpfToolkit:DataGridTextColumn Header=" Accounting Unit " Width="Auto"  
                                               IsReadOnly="True"
                                               Binding="{Binding Path=AccountingUnit, Mode=OneWay}"
                                                   />

                    </wpfToolkit:DataGrid.Columns>
                </wpfToolkit:DataGrid>
                <GridSplitter Grid.Row="1" Name="GridSplitter1" ResizeBehavior="PreviousAndNext" Height="3" HorizontalAlignment="Stretch" Foreground="DarkGray" Background="DarkGray"/>
                <StackPanel
                    Grid.Row="2"
                    IsEnabled="{u:Update ClientNavigation.IsClientSelected}"
                    >
                    <localViews:ClientEditView 
                        Foreground="White"
                        DataContext="{Binding ClientNavigation.CurrentClient, Mode=OneWay}"
                        VerticalAlignment="Stretch" 
                        HorizontalAlignment="Stretch" 
                        />
                </StackPanel>

 

Feb 10, 2010 at 4:52 AM

Just to see what would happen, try exposing the IsClientSelected property on the view model itself.

public bool IsClientSelected { get { return ClientNavigation.IsClientSelected; } }

Then you can change the binding to:

<StackPanel
    IsEnabled="{Binding IsClientSelected, Mode=OneWay}">
</StackPanel>
That shouldn't be necessary, but it might shed some light on the problem.

Feb 10, 2010 at 5:14 AM
Edited Feb 10, 2010 at 5:24 AM

Actually this is what I had before, and it was not working so I changed binding first to navigation model and then to u:Update, the latter works.

But I did try your suggestion again anyway, just to make sure I didn't miss anything the first time. It still does not work.

I was thinking that because I have about 3 layers in the hierarchy of ModelViews (will be more) and ForWrap is applied only to the root ViewModel some how references get lost. Maybe I should expose something via an interface. Note these are not Class hierarchy layers I am talking about, these are layers of how objects get created. MainViewModel creates ListsModel which in turn creates ClientsViewModel.

I admit I didn't look at your code yet, that would be my next step.

Thank you very much for your help.

Ivan

EDIT:

the only thing I was changing is in XAMP - Binding to u:Update. The C# code was still using ForWrap, not directly ViewModel to DataContext.

Here is how Navigation Model exposes CurrentClient and IsClientSelected properties:

    public class ClientNavigation : NavigationModelBase
    {

        private Client _currentClient;

        #region Independent properties
        // Generated by Update Controls --------------------------------
        private Independent _indIsClientSelected = new Independent();
        private Independent _indCurrentClient = new Independent();

        public Client CurrentClient
        {
            get { _indCurrentClient.OnGet(); return _currentClient; }
            set { _indCurrentClient.OnSet(); _currentClient = value; }
        }

        // End generated code --------------------------------
        #endregion
        public bool IsClientSelected
        {
            get {return CurrentClient != null; }
        }
}
Feb 10, 2010 at 4:30 PM

It looks like your code is correct. I have had success using the pattern in this way. Could you please email your code to me?

support@updatecontrols.net

Thanks.

Feb 10, 2010 at 5:43 PM

Michael,

Thanks for the offer, but I am sorry I can't send you my data, and I don't have time to write stubs for data classes right now.

This problem is not a show stopper (u:Update works) I just thought you might know off the top of your head.

On the other note. While using u:Update I found that it throws the following exceptions, which were not thrown while I was using Binding syntax.

System.Windows.Data Error: 22 : Cannot convert '<null>' from type '<null>' to type 'System.Boolean' for 'en-US' culture with default conversions; consider using Converter property of Binding. NotSupportedException:'System.NotSupportedException: BooleanConverter cannot convert from (null).
   at System.ComponentModel.TypeConverter.GetConvertFromException(Object value)
   at System.ComponentModel.TypeConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   at System.ComponentModel.BooleanConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)'
System.Windows.Data Error: 6 : 'ObjectSourceConverter' converter failed to convert value '<null>' (type '<null>'); fallback value will be used, if available. BindingExpression:Path=Value; DataItem='ValueDependencyObject' (HashCode=59408853); target element is 'StackPanel' (Name=''); target property is 'IsEnabled' (type 'Boolean') NotSupportedException:'System.NotSupportedException: BooleanConverter cannot convert from (null).
   at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)
   at MS.Internal.Data.ObjectSourceConverter.Convert(Object o, Type type, Object parameter, CultureInfo culture)
   at System.Windows.Data.BindingExpression.ConvertHelper(IValueConverter converter, Object value, Type targetType, Object parameter, CultureInfo culture)'

 From this I realized that object is null at the time view is created, while I could still hit debug point on the object property for some reason. :( If object is null why I am hitting object's property?

It looks like with u:Update it falls back to a default value = false, which happened to be the correct one for me, while with regular Binding the behavior was different.

So I created ObjectToBooleanConverter

    [ValueConversion(typeof(String), typeof(Boolean))]
    class ObjectToBooleanConverter : IValueConverter
    {
        public Object Convert(Object value, System.Type targetType, Object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null)
                return false;
            else
                return true;
        }

        public Object ConvertBack(Object value, System.Type targetType, Object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }

And placed it in XAML:

        <StackPanel
            Grid.Row="2"
            IsEnabled="{Binding Path=ClientNavigation.CurrentClient, Converter={StaticResource ObjectToBooleanConverter}}"
          >

This works very well. But I don't like writing converters, I would rather bind to the property of the class. That way it is more testable.

I don't know much about WPF, but I think IsEnabled property Binding occurs much earlier than all the other bindings and if bound object is null on a first pass it gets ignored afterwards as if there is no binding at all.

 

Feb 10, 2010 at 5:52 PM

Actually LINQ to SQL can generate db schema, but I don't have test data generator like in VS TSE, so I am using a copy of our production data.

So if you are still curious about my problem and can generate test data set, I can send you my project.

Feb 10, 2010 at 6:32 PM

Sure, I can generate some test data. Go ahead and send me what you can.

In the meantime, you do have an option that doesn't require a value converter. You could define a style for your stack panel with a data trigger.

    <Style x:Key="ClientPanelStyle" TargetType="StackPanel">
        <Style.Triggers>
            <DataTrigger Binding="{Binding ClientNavigation.CurrentClient}" Value="{x:Null}">
                <Setter Property="IsEnabled" Value="False"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
This is slightly preferable, since it is a XAML only solution.

 

Feb 13, 2010 at 12:43 AM

Michael,

I found what the problem was. It was really stupid, my bad, sorry for taking your time. I think you figured that by now. 
I used MVVM template for Visual Studio and it creates ViewModelBase and by default implements INotifyPropertyChanged interface.

So IsEnabled was bound to ClientViewModel whose base class had that interface while I was not firing PropertyChanged event.
XAML binding was listening for PropertyChanged events which never fired.
UpdateControls do not work on the class which implements INotifyPropertyChanged, I think you mentioned that before.

As soon as I took out INotifyPropertyChanged from base class, everything started working properly.

Thanks for your help.

Ivan

Feb 15, 2010 at 4:52 PM

Actually, I noticed that if class implements property of some interface, then Updatecontrols do not update that property.
I tried with other interfaces, regular UI binding to interface properties doesn't work well. The UI control needs to be invalidated before property will be refreshed.
As soon as interface declaration is removed from the class, these properties start working (e.g. updating).

I could be wrong, because I didn't see anything specific in your source code regarding interfaces.

Feb 16, 2010 at 3:47 PM

I've not had any problems implementing interfaces with my view models. But if the view model directly or indirectly implements INotifyPropertyChanged or INotifyCollectionChanged, then Update Controls will not wrap it. Make sure that your interface does not inherit one of these two.

Feb 16, 2010 at 3:49 PM

Ok, thanks.

Feb 16, 2010 at 6:49 PM

Actually, I think I might have found your problem.

public interface IThing
{
    string Name { get; }
}

public class Thing : IThing
{
    public string Name { get { return "Thing1"; } }
    public int Number { get { return 1; } }
}

public class ViewModel
{
    public IThing Thing1 { get { return new Thing(); } }
    public IEnumerable<IThing> Things { get { yield return new Thing(); } }
}

Update Controls will not see the property "Number". It is looking only at the properties exposed through IThing (i.e. "Name").

I just checked in a change that looks at the properties of the concrete type, not the declared type. Build this one and see if it works.

BTW, this problem is unrelated to the u:Update confusion, so it should have its own thread.

Feb 16, 2010 at 7:06 PM

Ok, thank you, Michael. I'll give it a try.