Set Source and ContentSource to a new instance

Aug 27, 2013 at 9:58 PM
Hello, I have another question,

I'm having to declare all of my user controls as New each time my main window opens, because of all all of the controls are accessed by other parts of my program.

The custom mui properties such as Source and ContentSource, used by Links and ModernWindows and what-not, is there a way to set them to new instances? Like, as of now, they are set by Uri strings. But I don't want to the Uri's point to the .xaml files, I want them to point to a specific instance of that .xaml I declare new.

Anyone have any idea how to go about this?
Coordinator
Aug 28, 2013 at 10:46 AM
You may want to asign attached property ModernFrame.KeepAlive=false to your usercontrol like so
<UserControl .. 
    xmlns:mui="http://firstfloorsoftware.com/ModernUI"
    mui:ModernFrame.KeepAlive="false">
  ..
</UserControl>
This ensures the DefaultContentLoader creates a new instance each time the control is loaded.
Aug 28, 2013 at 12:49 PM
Sounds similar to what I'm wanting to do. But the new instance I'm creating is via code and not XAML. I have several assemblies that access the same control via code, to control it's properties, such as enabling and disabling it's child controls. Is there a way I can set those XAML properties such as Source to the instance I create in code? If not, is there a work around to do what I;m looking for? I've tried looking into making the User Controls shared, but have been unsuccessful so far.
Aug 28, 2013 at 9:12 PM
Edited Aug 28, 2013 at 9:15 PM
Let me rephrase this. Basically, I'm wanting to know if there is a way to set the Source, ContentSource, and other source properties to already created objects; similar to how you would set the .Child property of an ElementHost o a WPF UserControl. That way, the control can be modified before setting it, and can also be modified after it's set.
Coordinator
Aug 30, 2013 at 2:43 PM
Edited Aug 30, 2013 at 2:44 PM
Looks like you want to implement your own IContentLoader. The DefaultContentLoader (assigned to ModernFrame, ModernTab and ModernWindows instances) loads xaml resources using Application.LoadComponent. You can create a custom IContentLoader that returns your own instances, including the ones you already created.

For more details about IContentLoader see:
Aug 30, 2013 at 9:37 PM
Sorry if there is an obvious answer to this question, but I seem to be struggling with something.

In the code,
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ContentAttribute : ExportAttribute
{
  public ContentAttribute(string contentUri) : base(typeof(IContent))
  {
    this.ContentUri = contentUri;
  }
  
  public string ContentUri { get; private set; }
}
I have got it converted to:
<MetadataAttribute> <AttributeUsage(AttributeTargets.[Class], AllowMultiple:=False)> _
Friend Class ContentAttribute
    Inherits ComponentModel.Composition.ExportAttribute

    Private m_ContentUri As String

    Public Property ContentUri() As String
        Get
            Return m_ContentUri
        End Get
        Private Set(value As String)
            m_ContentUri = value
        End Set
    End Property

    Public Sub New(ByVal contentUri As String)
        MyBase.New(GetType(IContent))
        Me.ContentUri = contentUri
    End Sub

End Class
However, types MetadataAttribute and IContent can't be found. I looked up the types online, and found the following reference for MetadataAttribute to be the following:

MetadataAttribute Class

However that reference was not found in my list of references, and wanted to make sure it was correct one before I downloaded the appropriate assembly.

As for the IContent, no leads so far.

I am by no means an expert programmer yet, still fairly a beginner.

Also, on the following:
[Content("/MyPage")]
public partial class MyPage: UserControl, IContent
{
  public void OnFragmentNavigation(FragmentNavigationEventArgs e)
  {
  }

  public void OnNavigatedFrom(NavigationEventArgs e)
  {
  }

  public void OnNavigatedTo(NavigationEventArgs e)
  {
  }

  public void OnNavigatingFrom(NavigatingCancelEventArgs e)
  {
  }
}
Does this need to be applied to "all" of my controls I want to do this with? (Not the event handlers obviously, but the inheritance).

Thanks for your help,
  • Jake M.
Coordinator
Sep 1, 2013 at 12:10 PM
IContent is a MUI interface defined in the FirstFloor.ModernUI.Windows namespace. MetadataAttribute is located in namespace System.ComponentModel.Composition (you need to reference BCL assembly System.ComponentModel.Composition.dll).

When using the MEF solution you'll need to implement IContent for all controls. This can be done relatively easy by introducing a base class that implements IContent (in fact I'm thinking of adding such class to mui).

Download the source from the MEF tutorial to see how it all fits together.
Sep 2, 2013 at 5:16 PM
Edited Sep 2, 2013 at 5:18 PM
When do you think such a release would be when that class is added?

I'm still a bit stuck, but I am making progress. I guess it's the fact I've never worked with Interfaces before, nor the attributes declared at the top.
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ContentAttribute : ExportAttribute
{
  public ContentAttribute(string contentUri) : base(typeof(IContent))
  {
    this.ContentUri = contentUri;
  }
  
  public string ContentUri { get; private set; }
}
For that particular class, I have:
<ComponentModel.Composition.MetadataAttribute> _
<AttributeUsage(AttributeTargets.[Class], AllowMultiple:=False)> _
Friend Class ContentAttribute
    Inherits ComponentModel.Composition.ExportAttribute

    Private m_ContentUri As String

    Public Property ContentUri() As String
        Get
            Return m_ContentUri
        End Get
        Private Set(value As String)
            m_ContentUri = value
        End Set
    End Property

    Public Sub New(ByVal contentUri As String)
        MyBase.New(GetType(FirstFloor.ModernUI.Windows.IContent))
        Me.ContentUri = contentUri
    End Sub

    Public Interface IContentMetadata
        ReadOnly Property ContentUri() As String
    End Interface

End Class
But even though there are no errors, I feel uncomfortable with what I have as being correct. I'm not native to C#.

Also, the following class I have no idea what's going on at all:
[Export]
public class MefContentLoader : DefaultContentLoader
{
  [ImportMany]
  private Lazy<IContent, IContentMetadata>[] Contents { get; set; }

  protected override object LoadContent(Uri uri)
  {
    // lookup the content based on the content uri in the content metadata
    var content = (from c in this.Contents
      where c.Metadata.ContentUri == uri.OriginalString
      select c.Value).FirstOrDefault();

    if (content == null) {
      throw new ArgumentException("Invalid uri: " + uri);
    }

    return content;
  }
}
And lack the capability of converting it properly to VB.

As for the other classes such as IContentMetaData, I believe I have done and declared correctly, with the exception of knowing what to put exactly in the top attribute,
[Content("/MyPage")]
In the custom User Control class.

I know I have changed some of the modifiers from Public to Friend (mostly structural preference), but I don't think for my purposes, it would affect performance.

So I guess what I'm asking is that:
  • Are there are any examples in VB for the two classes (or the guide itself) that I am struggling with
  • When will this be natively implemented into ModernUI?
Sep 11, 2013 at 10:00 PM
Edited Sep 11, 2013 at 10:01 PM
I've done a lot of research and I'm still confused on how MEF can help me with this. As it seems, you still need to set the Source property to the correct path of the URI in your Solution. Unless I am missing something here, how does this relate to reloading the same instance of a certain user control, each time the Source property is set (a specific instance declared in code, for that matter)?

I've looked through the sample dozens of times, toyed around with a thing or two, but still don't see where in the code it does this.
Sep 20, 2013 at 7:51 PM
I've resolved my issue with a workaround of loading properties external variables, and setting those variables to the properties of controls at the startup of page navigation.