Prism v4, MEF, WPF - Using proper Uri for module graphic - wpf

My solution's structure is:
CI.Frontier.Classic contains a MEF module. My application uses the RibbonWindow control, and the modules define what menu items should be created. I can successfully add a button to the ribbon control from the CI.Frontier.Classic module, however, I cannot figure out the proper Uri to ClassicFrontierToopTip.png
Heres the code in FrontierClassic.cs that creates the tab, button and attempting to set the ribbon ToolTipImage
public void CreateMenuItems()
{
TabData tabData = new TabData("Legacy");
GroupData groupData = new GroupData("Internal");
tabData.GroupDataCollection.Add(groupData);
ButtonData classicFrontierBtn = new ButtonData()
{
Label = "Classic Frontier",
ToolTipTitle = "Classic Frontier",
ToolTipDescription = "Open Classic Frontier",
ToolTipImage = new Uri("./Graphics/ClassicFrontierToolTip.png", UriKind.Relative)
};
classicFrontierBtn.Command.RegisterCommand(new DelegateCommand(LoadFrontierView));
groupData.ControlDataCollection.Add(classicFrontierBtn);
_ribbonService.AddTab(tabData);
}
This Uri doesn't work as the tooltip does not display. Can I use the UriKind.Relative or should I be using some sort of "pack uri"?

The robust approach would be to leverage the pack syntax...
new Uri("pack://application:,,,/CI.Frontier.Classic;component/Graphics/ClassicFrontierToolTip.png", UriKind.Absolute);

Include an icon for a Prism module solved it...
ToolTipImage = new Uri("/" + GetType().Assembly.ToString().Split(',')[0] + ";component/Graphics/ClassicFrontierToolTip.png", UriKind.Relative)
Not sure if this is the best solution though.

Related

How to remove/disable the print button from the DocumentViewer in code (when on its own thread)?

I am running the following code in a background thread as an STA apartment to provide a Print Preview in a document viewer:
// Print Preview
public static void PrintPreview(FixedDocument fixeddocument)
{
MemoryStream ms = new MemoryStream();
using (Package p = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite))
{
Uri u = new Uri("pack://TemporaryPackageUri.xps");
PackageStore.AddPackage(u, p);
XpsDocument doc = new XpsDocument(p, CompressionOption.Maximum, u.AbsoluteUri);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
writer.Write(fixeddocument.DocumentPaginator);
var previewWindow = new Window();
var docViewer = new DocumentViewer();
previewWindow.Content = docViewer;
THIS FAILS ---> docViewer.CommandBindings.Remove(???Print Button???);
FixedDocumentSequence fixedDocumentSequence = doc.GetFixedDocumentSequence();
docViewer.Document = fixedDocumentSequence as IDocumentPaginatorSource;
previewWindow.ShowDialog();
PackageStore.RemovePackage(u);
doc.Close();
}
}
All works well. However, since this is running in its own thread--not the main thread--the print dialogue on the Document Viewer crashes.
In code, how can I remove and/or disable the Print button from the DocumentViewer?? (I have read everything I could find in Google, and it all is in XAML, not very helpful).
Any help is much appreciated. TIA
Update#1: The only way I can see to do this, is to drop the Print Button from the control template and use a custom Document Viewer. A workable style is given at Document Viewer Style.
It still would be nice if I could simply remove the button from the system Document viewer?
Using the method from this answer, you can alter the visibility of the PrintButton programmatically like this. Let's say I put the method in a class called UIElementHelper:
var button = UIElementHelper.FindChild<Button>(docViewer, "PrintButton");
button.Visibility = Visibility.Collapsed;

Create View from ViewModel object

I see that MvvmCross touch supports creating a View from a ViewModel object using a MvxViewModelRequest.
But in MvvmCross WPF, I can only create Views from a MvxViewModelRequest using
Mvx.Resolve<IMvxSimpleWpfViewLoader>().CreateView(viewmodelRequest)
However, I cannot find a way to create a View from a ViewModel object? Is this support in MvvmCross for WPF?
This functionality isn't included by default in Wpf - but you could easily add it.
The logic would be similar to the request-based code in https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Wpf/Views/MvxWpfViewsContainer.cs - something like:
// Use `IMvxViewFinder` to find the type of view:
var viewType = Mvx.Resolve<IMvxViewFinder>().GetViewType(myViewModel.GetType());
// create a view and set the data context
var viewObject = Activator.CreateInstance(viewType);
if (viewObject == null)
throw new MvxException("View not loaded for " + viewType);
var wpfView = viewObject as IMvxWpfView;
if (wpfView == null)
throw new MvxException("Loaded View does not have IMvxWpfView interface " + viewType);
wpfView.ViewModel = myViewModel;
You could build this into a custom views container or a custom view presenter if you wanted to.
Assume u have
public partial class LoginViewController : MvxViewController<LoginViewModel>
than, if i want to use view somewhere u can do something like
this.presentedCurrentController = Activator.CreateInstance(typeof(LoginViewController)) as LoginViewController;
(this.presentedCurrentController as LoginViewController).ViewModel = new LoginViewModel();
where
this.presentedCurrentController it's
var NSViewController presentedCurrentController;
Thanks too #cheesebaron for link and another one

How to detect the sender(button) of dynamical created bindings

I create some RibbonButtons dynamically and add them to a group according to an xml file. The follwoing function is carried out as often as entries found in the xml file.
private void ExtAppsWalk(ExternalAppsXml p, AppsWalkEventArgs args)
{
RibbonButton rBtn = new RibbonButton();
rBtn.Name = args.Name;
Binding cmdBinding = new Binding("ExtAppCommand");
rBtn.SetBinding(RibbonButton.CommandProperty, cmdBinding);
Binding tagBinding = new Binding("UrlTag");
tagBinding.Mode = BindingMode.OneWayToSource;
rBtn.SetBinding(RibbonButton.TagProperty, tagBinding);
rBtn.Label = args.Haed;
rBtn.Tag = args.Url;
rBtn.Margin = new Thickness(15, 0, 0, 0);
MyHost.ribGrpExtern.Items.Add(rBtn);
}
I tried to use the Tag property to store the Url's to be started when the respective button is clicked. Unfortunately the binding to the Tag property gives me the last inserted Url only.
What would be the best way to figure out which button is hit or to update the Tag property.
The datacontext is by default the context of the Viewmodel. The RibbonGroup to which the Buttons are added is created in the xaml file at designtime. I use that construct:
MyHost.ribGrpExtern.Items.Add(rBtn);
to add the buttons. It maight not really be conform with the mvvm pattern. May be someone else has a better idea to carry that out.
I foud a solution for my problem here and use the RelayCommand class. So I can pass objects (my Url) to the CommandHandler.
RibbonButton rBtn = new RibbonButton();
rBtn.Name = args.Name;
Binding cmdBinding = new Binding("ExtAppCommand");
rBtn.SetBinding(RibbonButton.CommandProperty, cmdBinding);
rBtn.CommandParameter = (object)args.Url;
private void ExtAppFuncExecute(object parameter)
{
if (parameter.ToString().....//myUrl

Design time data in WPF

[using vs2010 & expression blend v4]
Hi - trying to load up some design time data in WPF and Blend, using Josh Smith's concept here: http://joshsmithonwpf.wordpress.com/2010/04/07/assembly-level-initialization-at-design-time/
e.g.
[AttributeUsage(AttributeTargets.Assembly)]
public class DesignTimeBootstrapperAttribute : Attribute
{
public DesignTimeBootstrapperAttribute(Type type)
{
var dep = new DependencyObject();
Debug.WriteLine("here..?");
if (DesignerProperties.GetIsInDesignMode(dep))
{
// TODO: Design-time initialization…
IBootstrapper instance = Activator.CreateInstance(type) as IBootstrapper;
if (instance != null)
{
instance.Run();
}
}
}
}
With my attribute here in AssemblyInfo.cs, where AppBootstrapper extends MefBootstrapper.
[assembly: AssemblyCopyright("Copyright © 2010")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: DesignTimeBootstrapper(typeof(AppBootstrapper))]
I don't want to use the Blend sample data, a) as it doesn't seem to create data for ObservableCollection and b) I'm in design mode by definition, so things will change quite a lot, but my 'generated data' will not.
Anyway, nothing seems to be happening.
Q1: How is it possible to debug the design time initialisation of my bootstrapper?
Q2: Do I need additional blend namespaces/ attributes etc in my View XAML?
(In my bootstrapper I'm just registering a different module where I want to replace RunTimeService with a DesignTimeService, exporting the IService interface).
TIA
To debug this:
Open your project in VS2010
Set a breakpoint in the assembly attribute constructor
Start a new instance of Blend 4
From VS2010 use Debug -> Attach to Process: and choose Blend
Switch to Blend and open your project
Open a XAML file that references your sample data
Also, any Debug.WriteLine should appear in the VS2010 output window.
If you can't get the attribute method to work (I haven't tried it myself), you can use this method (which I have used) from MVVM Light:
private bool? _isInDesignMode;
public bool IsInDesignMode
{
get
{
if (!_isInDesignMode.HasValue)
{
var prop = DesignerProperties.IsInDesignModeProperty;
_isInDesignMode =
(bool)DependencyPropertyDescriptor
.FromProperty(prop, typeof(FrameworkElement))
.Metadata.DefaultValue;
}
return _isInDesignMode.Value;
}
}

SIlverlight Navigate: how does it work? How would you implement in f# w/o VS wizards and helpers?

update 5: brians solution worked:
namespace Module1
type Page1() as this =
inherit UserControl()
let uriStr = "/FSSilverlightApp;component/Page1.xaml"
let uri = new System.Uri(uriStr, System.UriKind.Relative)
do
Application.LoadComponent(this, uri)
member public this.Uri with get () = uri
type MyApp() as this =
inherit Application()
do Application.LoadComponent(this, new System.Uri("/FSSilverlightApp;component/App.xaml", System.UriKind.Relative))
let nav : Frame = siteTemplate ? contentFrame
let p1 = new Module1.Page1() ;
member this.navigate ea =
nav.Navigate(p1.Uri)
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Module1.MyApp">
<Application.Resources>
</Application.Resources>
</Application>
<UserControl x:Class="Module1.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="This is page 1 Lets see if we can ever get here!!!" FontSize="24" />
</Grid>
</UserControl>
update 4:
the template Brian has mentioned is mostly doing the trick. I still have a more complex page that is giving me troubles - yet it is most likely my code. Once my code is complete I will post what I can diagnose but the parts include:
Setting App.XAML correctly (referencing your application object correctly)
in post construction of your application object use Application.Load to load the App.xaml
In your application object create instances of your page xaml
in post construction of your page objects use Application.Load to load the individual page.xaml
each of your page objects should extend UserControl; I suspect this is not truly the case - once I get more more complex page running I will see if removing this restriction will have an effect.
update 3:
I implemented my own controller logic in the Application object, which seems to do part of the trick (and solve my needs for a prototype anyhow).
type Page1() as this =
inherit Page()
do
this.Content <- loadXaml("Page1.xaml")
type MyApp() as this =
inherit Application()
let cc = new ContentControl()
let mainGrid : Grid = loadXaml("MainWindow.xaml")
let siteTemplate : Grid = if mainGrid.Name = "siteTemplate" then mainGrid else mainGrid ? siteTemplate
let nav : Frame = siteTemplate ? contentFrame
let page1 = new Module1.Page1() :> Page ;
let page2 = new Module1.Page2() :> Page ;
let page3 = new Module1.Page3() :> Page ;
do
this.Startup.Add(this.startup)
// to be able to get focus
cc.IsTabStop <- true
cc.IsEnabled <- true
System.Windows.Browser.HtmlPage.Plugin.Focus()
cc.Content <- mainGrid
this.RootVisual <- cc
member this.startup ea =
menu.MenuItemClicked.Add(this.navigate)
resolutionSlider.SizeChanged.Add(this.resizeTemplate)
member this.navigate ea =
if ea.Index = 1 then nav.Content <- page1
elif ea.Index = 2 then nav.Content <- page2
elif ea.Index = 3 then nav.Content <- page3
It works... I don't know the implication on memory / performance. I wonder if the navigation fw handles the construction / destruction of page objects more efficiently than what I did. I think the navigation FW works nicely with the browsers back and forward buttons - which my solution doesn't.
update 2: it looks as though teh C# applciation implments
public void InitializeComponent()
which loads and the XAML. Though I am no IL expert; I will make the similar changes on the F# side... I wonder if it is the partial class concept. One theory I am working on is:
page.xaml.cs is definitely a partial class - you can read it in the source.
page.xaml has an attribute that refers back to the c# class. I wonder if the special build commands treat this as a partial class - by parsing it and creating 1) any member component references 2) intialComponent() method which registers the page wherever it needs to be registered?
Update 1: After a nights sleep the problem can be stated more accurately as I have a 100% f# / silverlight implementation and am looking to use the built in Navigation components. C# creates page.xaml and page.xaml.cs um - ok; but what is the relationship at a fundamental level? How would I go about doing this in f#?
The applcuation is loaded in the default module, and I pull the XAML in and reference it from the application object. Do I need to create instances / references to the pages from within the application object? Or set up some other page management object with the proper name value pairs?
When all the Help of VS is stripped away - what are we left with?
original post (for those who may be reading replies)
I have a 100% silverlight 3.0 / f# 2.0 application I am wrapping my brain around. I have the base application loading correctly - and now I want to add the naigation controls to it.
My page is stored as an embedded resource - but the Frame.Navigate takes a URI. I know what I have is wrong but here it is:
let nav : Frame = mainGrid ? mainFrame
let url = "/page1.xaml"
let uri = new System.Uri(url, System.UriKind.Relative) ;
nav.Navigate uri
Any thoughts?
Have you tried making the Xaml a file in the project with a BuildAction of Content rather than an EmbeddedResource? Honestly, I've no clue if that works, but it might get packaged into the .xap that way, and then the relative uri might work. How would it work in a C# project? Try that.
EDIT
Aha, Dmitry's template appears to have this figured out. He has Xaml files with BuildAction of Resource, and then code like
type MainPage() as this =
inherit UserControl()
do
Application.LoadComponent(this,
new System.Uri("/SilverlightApplication3;component/Page.xaml",
System.UriKind.Relative))
let layoutRoot : Grid = downcast this.FindName("LayoutRoot")
do
()
to load it.

Resources