New Posts New Posts RSS Feed: Binding to visual objects in the model?
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Binding to visual objects in the model?

 Post Reply Post Reply
Author
Siyfion View Drop Down
Groupie
Groupie
Avatar

Joined: 22-Mar-2012
Location: Bristol, UK
Posts: 47
Post Options Post Options   Quote Siyfion Quote  Post ReplyReply Direct Link To This Post Topic: Binding to visual objects in the model?
    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...


But I'm still struggling to see how I can put this all together... Any help / tips / pointers / opinions mucho appreciated!
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post 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.
Back to Top
Siyfion View Drop Down
Groupie
Groupie
Avatar

Joined: 22-Mar-2012
Location: Bristol, UK
Posts: 47
Post Options Post Options   Quote Siyfion Quote  Post ReplyReply Direct Link To This Post 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?
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post 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.
Back to Top
Siyfion View Drop Down
Groupie
Groupie
Avatar

Joined: 22-Mar-2012
Location: Bristol, UK
Posts: 47
Post Options Post Options   Quote Siyfion Quote  Post ReplyReply Direct Link To This Post 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..?
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post 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.
Back to Top
Siyfion View Drop Down
Groupie
Groupie
Avatar

Joined: 22-Mar-2012
Location: Bristol, UK
Posts: 47
Post Options Post Options   Quote Siyfion Quote  Post ReplyReply Direct Link To This Post 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>



Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post 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(v=vs.100).aspx
 
Your binding won't work at all. You'll have to employ a ValueConverter in your binding to convert your string to the correct FontWeight. Bold for example is FontWeight.Bold. You can't use the type FontWeight directly in an entity model, so you'll have to go through a ValueConverter.
 
Back to Top
Siyfion View Drop Down
Groupie
Groupie
Avatar

Joined: 22-Mar-2012
Location: Bristol, UK
Posts: 47
Post Options Post Options   Quote Siyfion Quote  Post ReplyReply Direct Link To This Post 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?
Back to Top
Siyfion View Drop Down
Groupie
Groupie
Avatar

Joined: 22-Mar-2012
Location: Bristol, UK
Posts: 47
Post Options Post Options   Quote Siyfion Quote  Post ReplyReply Direct Link To This Post 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 System.Windows.Media.FontFamily.
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post 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. 
Back to Top
Siyfion View Drop Down
Groupie
Groupie
Avatar

Joined: 22-Mar-2012
Location: Bristol, UK
Posts: 47
Post Options Post Options   Quote Siyfion Quote  Post ReplyReply Direct Link To This Post 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?
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post 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.
Back to Top
Siyfion View Drop Down
Groupie
Groupie
Avatar

Joined: 22-Mar-2012
Location: Bristol, UK
Posts: 47
Post Options Post Options   Quote Siyfion Quote  Post ReplyReply Direct Link To This Post Posted: 18-Apr-2012 at 1:21am
Ok thanks! :D
Back to Top
Siyfion View Drop Down
Groupie
Groupie
Avatar

Joined: 22-Mar-2012
Location: Bristol, UK
Posts: 47
Post Options Post Options   Quote Siyfion Quote  Post ReplyReply Direct Link To This Post 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 for more info.
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down