Print Page | Close Window

Binding to visual objects in the model?

Printed From: IdeaBlade
Category: Cocktail
Forum Name: Community Forum
Forum Discription: A professional application framework using Caliburn.Micro and DevForce
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=3377
Printed Date: 28-Apr-2024 at 2:51am


Topic: Binding to visual objects in the model?
Posted By: Siyfion
Subject: Binding to visual objects in the model?
Date Posted: 05-Apr-2012 at 5:39am
Ok, so the title might be a bit confusing, so I'll do my best to explain...

Imagine an application with a large amount of graphical interactivity, where users can use tools to modify the way a product looks, or how the packaging is designed, etc. Perhaps even think in terms of a WPF MVVM "Paint".

In WPF the obvious way (I think at least) to allow users to manipulate the designs is using a Canvas control. When they have finished designing the "look" it needs to be saved to that users data so that they can "use" their design later on, or come back and modify it again later. This Canvas can be saved to an entity then as either a binary object, or perhaps better still, as a string containing the "raw" xaml, which can then be parsed in/out.

To add a bit more complexity, some of the things that the user may have positioned / resized / etc. are linked to data properties, let's go back to my old DVD example; say the user is designing the cover art for a DVD they have just uploaded, they want to be able to select from a range of "template" DVD covers which are essentially just Canvases that contain text fields that are linked to underlying properties of the DVD. eg. a text field called "Title" doesn't get printed with the word "Title" it is replaced with the DVDs title property.

I'm struggling as to how best to achieve this sort of behaviour, in terms of how the data bindings should work, what objects should potentially be there-own Views / ViewModels, etc. Also, I'm not sure if saving them in the way I described is in-fact the best way to store it all...

I've taken a look at this:  http://stackoverflow.com/questions/975602/mvvm-pattern-for-a-paint-like-wpf-application - http://stackoverflow.com/questions/975602/mvvm-pattern-for-a-paint-like-wpf-application  

But I'm still struggling to see how I can put this all together... Any help / tips / pointers / opinions mucho appreciated!



Replies:
Posted By: mgood
Date Posted: 05-Apr-2012 at 6:33pm
Not your typical application scenario, but I think that post is a good start. The various visual objects you want to render on the canvas could be entities (since DevForce entities can be directly bound to the UI), that you store in the database and then define implicit data templates for each type, so XAML knows how to render/bind them on the canvas through the ItemsControl as shown in the post. Implicit data templates are available in WPF and Silverlight 5.


Posted By: Siyfion
Date Posted: 13-Apr-2012 at 9:15am
Ok, I've set up a bit of an example project using this approach, so far so good... 
I like the fact it doesn't involve storing *.xaml in the database and then using that in effect (once it's parsed) as a view, which seems very wrong in an MVVM app.

However, I have some questions regarding the notification of a property that's been updated in the Entity... Say I have an entity called "IsBold" and it's a boolean; if I have a function called "MakeBold()" in the entity that sets the property to true, how does the Binding get notified that it has changed?


Posted By: mgood
Date Posted: 13-Apr-2012 at 11:58am
Through the mechanism of INotifyPropertyChanged and INotifyCollectionChanged in case the property is a collection. DevForce entities are bindable, which means they implement the above interfaces among other things. If you build your own objects or ViewModels and want to bind to them you have to ensure that INPC and INCC are implemented and that the events are fired whenever a property changes.


Posted By: Siyfion
Date Posted: 16-Apr-2012 at 8:54am
Sorry, I wasn't very clear then... I have created a view for a DevForce entity, and I have added a function to the partial class called "MakeBold()" which changes the scalar property of the entity to true, however the view doesn't seem to update accordingly..?


Posted By: mgood
Date Posted: 16-Apr-2012 at 9:07am
Is that property mapped to the database is it your own calculated property? Maybe you can post the necessary parts of that entity. It should trigger the PropertyChanged event when you set the scalar property from MakeBold(). Also, when you say scalar property, what type is it and how is it bound in View. From your description alone, I don't know what would be wrong.


Posted By: Siyfion
Date Posted: 17-Apr-2012 at 4:28am
My *.edmx diagram is as follows:


And the code snippets:

namespace DataModel
{
    public partial class MatrixEntity
    {
        internal MatrixEntity() { }

        public static MatrixEntity Create()
        {
            var matrixEntity = new MatrixEntity
            {
                m11 = 1.0, m12 = 0.0,
                m21 = 0.0, m22 = 1.0,
                offsetX = 0.0, offsetY = 0.0
            };

            return matrixEntity;
        }

        public Matrix Matrix
        {
            get
            {
                return (new Matrix(m11, m12, m21, m22, offsetX, offsetY));
            }
            set
            {
                m11 = value.M11;
                m12 = value.M12;
                m21 = value.M21;
                m22 = value.M22;
                offsetX = value.OffsetX;
                offsetY = value.OffsetY;
            }
        }
    }

    public partial class TextBlockEntity
    {
        internal TextBlockEntity() { }

        public static TextBlockEntity Create()
        {
            var textBlockEntity = new TextBlockEntity();
            textBlockEntity.MatrixEntity = MatrixEntity.Create();
            return textBlockEntity;
        }

        public void SetBold()
        {
            this.FontWeight = "Bold";
        }
    }
}

Some Xaml:

<UserControl x:Class="WpfApplication2.Views.TextBlockEntityView"
             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:cal="http://www.caliburnproject.org"
             mc:Ignorable="d"
             Height="Auto" Width="Auto">

    <UserControl.RenderTransform>
        <MatrixTransform Matrix="{Binding MatrixEntity.Matrix}"/>
    </UserControl.RenderTransform>

    <TextBlock FontSize="{Binding Path=FontSize, FallbackValue=50}"
               FontFamily="{Binding Path=FontFamily, FallbackValue=Impact}"
               FontStyle="{Binding Path=FontStyle, FallbackValue=Normal}"
               FontWeight="{Binding Path=FontWeight, FallbackValue=Normal}"
               TextDecorations="{Binding Path=TextDecorations}"
               Text="{Binding Path=Text, FallbackValue=Some Sample Text}">
        
        <TextBlock.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Bold"
                          IsCheckable="True"
                          cal:Message.Attach="[Event Checked] = [Action SetBold]"/>
            </ContextMenu>
        </TextBlock.ContextMenu>
    </TextBlock>

</UserControl>





Posted By: mgood
Date Posted: 17-Apr-2012 at 8:41am
That looks like a type mismatch in your binding. The FontWeight property of a Textbox is not a string even though it might look like it from looking at any XAML that sets the FontWeight. The FontWeight property is of type FontWeight.
http://msdn.microsoft.com/en-us/library/system.windows.controls.control.fontweight%28v=vs.100%29.aspx - - http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx


Posted By: Siyfion
Date Posted: 17-Apr-2012 at 8:48am
I know this may sound stupid, but surely the View shouldn't know about the underlying data type? So shouldn't the value conversion be done in the VM (admittedly, there isn't one as yet in this example!) rather than specified in the binding in the View?


Posted By: Siyfion
Date Posted: 17-Apr-2012 at 8:53am
Also, I'm binding to FontFamily the same way and the binding does seem to work, it may not notify on update however... And I know that the type isn't really a string, it's really a  http://msdn.microsoft.com/en-us/library/system.windows.media.fontfamily.aspx - System.Windows.Media


Posted By: mgood
Date Posted: 17-Apr-2012 at 9:14am
Originally posted by Siyfion

I know this may sound stupid, but surely the View shouldn't know about the underlying data type? So shouldn't the value conversion be done in the VM (admittedly, there isn't one as yet in this example!) rather than specified in the binding in the View?

It doesn't. It just knows about the ValueConverter the same way it knows about the ViewModel. The details are hidden in the ValueConverter. Converting directly in the ViewModel is another option if you have a ViewModel in between. I personally don't like using visual types in the ViewModels, because that means my ViewModel needs to know what control I'm actually using in the View. It shouldn't have to know that. The ValueConverter keeps these details independent. 


Posted By: Siyfion
Date Posted: 17-Apr-2012 at 9:24am
But the very act of putting a ValueConverter onto the binding surely implies that the View has "knowledge" that the data will be in an incompatible format, doesn't it?

Also, if that is what you're suggesting, where would you recommend putting the ValueConverter, in terms of solution modules, etc?


Posted By: mgood
Date Posted: 17-Apr-2012 at 10:05am
That is correct. That's no different from using a DataTemplate to translate a type structure to a visual tree. The View always knows the ViewModel and how it exposes the data. That extends to the types since .NET has a strongly typed type system. In pure MVVM, the ViewModel should never know the View, though.

TempHire uses a couple of ValueConverters. They are in the Common project. If you want to go even cleaner, create a separate project for the ValueConverters, Behaviors etc.


Posted By: Siyfion
Date Posted: 18-Apr-2012 at 1:21am
Ok thanks! :D


Posted By: Siyfion
Date Posted: 19-Apr-2012 at 6:44am
If anyone else is reading this, the reason why the FontWeight binding did work (partially) when the type was a string, is because there is a Default Type Converter on the FontWeight property that handles the conversion, see  http://msdn.microsoft.com/en-us/library/system.windows.fontweightconverter.aspx - http://msdn.microsoft.com/en-us/library/system.windows.fontweightconverter.aspx  for more info.



Print Page | Close Window