The LayoutManger's ShowDocument method calls OnOpened on the document that calls ShowDocument. However, CreateDocument is intended to return a different document when used as a multiple document manager. The result is, the document that is actually opened is the one that is returned by CreateDocument, but the the OnOpened call is sent to a document that is not actually shown. So I changed the line

document.OnOpened(content, new EventArgs());

to

doc.OnOpened(content, new EventArgs());

and it works in the way that makes sense to me (which might not actually be what was intended).

I am still having an issue where the view's xaml correctly acts like an instance, but the code behind acts like a singleton. For example... I have a button defined in the xaml that is linked to an event handler in code behind. The event handler changes a property in the view model. The result is, only one instance ever gets changed, regardless of which instance's button I click. The instance that gets changed is the one whose button is first clicked. Thereafter, clicking the button on ANY instance only changes the SAME instance again (the one that was previously changed). I'm not a WPF guru, so I'm not sure whether the issue is in SoapBox, AvalonDock, or WPF, or in some way I have something of mine configured. It sounds like it MIGHT be an issue of StaticResource vs DynamicResource. I'm still investigating...

asked 26 Apr '12, 11:18

drewkeller's gravatar image

drewkeller
76124
accept rate: 0%

edited 26 Apr '12, 20:06

Scott%20Whitlock's gravatar image

Scott Whitlock ♦♦
696262833

The first part of your question looks like a bug, so I tagged this question with the status-planned, so I'll come around and look at it again.

(26 Apr '12, 20:07) Scott Whitlock ♦♦

Here's a fairly quick workaround.... I intend to update my CodeProject article (or add another) with a working example.

In the document, add a static method to look up an instance from our document dictionary.

public static Recipe GetDocument(string memento)
{
    return _docs[memento];
}

In the view's xaml, add an item that is bound to the view model's memento (hide it if you don't want to see it).

<TextBlock Name="txtMemento" Text="{Binding Memento}" Visibility="Hidden"/>

In the code-behind of the view's resource dictionary, add a property whose getter looks up the document.

private Recipe Model { get { return Recipe.GetDocument(this.txtMemento.Text); } }

Then all references to the Model (ahem ViewModel) will look up the correct one.

private void btnTest_Click(object sender, RoutedEventArgs e)
{
    this.Model.SomeProperty += "\\nTested";
}
link

answered 26 Apr '12, 11:50

drewkeller's gravatar image

drewkeller
76124
accept rate: 0%

I havnt figured out how to get the "more normal way" with UserControl to work without putting it inside of a ResourceDictionary. Do you have an existing example or can you describe some more details?

The Applying Views to ViewModels section of your CodeProject article says "The Host imports a collection of ResourceDictionary extensions on startup, and manually inserts them into the application resources". I suppose a host could look for some other type (IView or AbstractView or somesuch) which each view would inherit or implement. Then I'm not sure what container the DataContext should go in.

(02 May '12, 23:43) drewkeller

For the second part of your question, I assume you're using a DataTemplate. I'll also assume you've got that DataTemplate defined in a ResourceDictionary. So the code-behind you're talking about is probably the code-behind of the ResourceDictionary. In that case, yes, there is only ever one instance of the ResourceDictionary.

When WPF sees your ViewModel in the visual tree, it replaces it with a new visual tree of objects created from your DataTemplate (not the actual objects in the template itself - it's just a template). The event handlers of those new objects are hooked up to the event handlers you defined in the code-behind of the ResourceDictionary, but from their point of view, this refers to the instance of the ResourceDictionary. In effect, the event handlers are calling `someResourceDict.btnTest_Click()'.

You can still do this, but you have to do it like this:

private void btnTest_Click(object sender, RoutedEventArgs e)
{
    var btn = sender as Button;
    if(btn == null) return; // should never happen, but nice to do

    var model = btn.DataContext as Recipe;
    if(model == null) return; // WPF wires up the DataContext, so it should be right

    this.Model.SomeProperty += "\\\\nTested";
}

The other, more "normal" way would be to create your View as a UserControl, and then use the UserControl in the DataTemplate. A new instance of the UserControl will be created for each instance of your document. Therefore you can write the following in the code-behind of the UesrControl:

private void btnTest_Click(object sender, RoutedEventArgs e)
{
    var model = this.DataContext as Recipe;
    if(model == null) return;

    this.Model.SomeProperty += "\\\\nTested";
}

Alternately you can hook the DataContextChanged event and store it as a private member variable, but I find that comes with its own issues.

link

answered 26 Apr '12, 20:27

Scott%20Whitlock's gravatar image

Scott Whitlock ♦♦
696262833
accept rate: 50%

edited 26 Apr '12, 20:29

Thanks for the explanation.

For the second part of your question, I assume you're using a DataTemplate. I'll also assume you've got that DataTemplate defined in a ResourceDictionary. So the code-behind you're talking about is probably the code-behind of the ResourceDictionary. In that case, yes, there is only ever one instance of the ResourceDictionary.ResourceDictionary.

All of the SoapBox examples I've seen do it this way. If there's another way, I'd like to try it.

Actually, the method used in my CodeProject article is to use a ResourceDictionary wrapping a DataContext wrapping a UserControl. That way, you can still get a GUI to edit the control. The code-behind I was referring to was actually in the UserControl. The forum software seems to have difficulty with the formatting in xaml, so here's an abbreviated pseudo-version.

RecipeView.xaml

ResourceDictionary 
x:Class="Example.HelloWorld.Documents.RecipeView"
xmlns:local="clr-namespace:Example.HelloWorld.Documents"

    DataTemplate DataType="{x:Type local:Recipe}"
       local:RecipeViewControl

RecipeView.xaml.cs

(using statements blah blah)
namespace Example.HelloWorld.Documents
{
    [Export(SoapBox.Core.ExtensionPoints.Host.Views, typeof(ResourceDictionary))]
    public partial class RecipeView : ResourceDictionary
    {
        public RecipeView()
        {
            InitializeComponent();
        }

        // I figured out I had to do this to get some twoway binding to work between tab changes
        public void Content_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            var newModel = (Recipe)e.NewValue;
            if (newModel == null) return;
            var view = (RecipeViewControl)sender;
            view.TabChanged(Recipe.GetDocument(newModel.Memento));
        }
    }
}

RecipeViewControl.xaml

(nothing special here, just regular UserControl stuff)
(well, ok, I have a hidden text field bound to the memento as described in my original post)
(and a button that calls the Click event in the code-behind below)

RecipeViewControl.xaml.cs

(using statements blah blah)
namespace Example.HelloWorld.Documents
{
    /// <summary>
    /// Interaction logic for RecipeViewControl.xaml
    /// </summary>
    public partial class RecipeViewControl : UserControl
    {
        private Recipe Model { get { return Recipe.GetDocument(this.txtMemento.Text); } }

        public RecipeViewControl()
        {
            InitializeComponent();
        }

        private void btnTest_Click(object sender, RoutedEventArgs e)
        {
            this.Model.SomeProperty += "\\nTested";
        }

        public void TabChanged(Recipe document)
        {
        }

    }
}
link

answered 27 Apr '12, 18:11

drewkeller's gravatar image

drewkeller
76124
accept rate: 0%

Your answer
toggle preview

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • link:[text](http://url.com/ "Title")
  • image?![alt text](/path/img.jpg "Title")
  • numbered list: 1. Foo 2. Bar
  • to add a line break simply add two spaces to where you would like the new line to be.
  • basic HTML tags are also supported

Tags:

×23
×6
×5

Asked: 26 Apr '12, 11:18

Seen: 4,845 times

Last updated: 02 May '12, 23:43

powered by OSQA