I'm trying to add a property to the WPF web browser to allow me to use NavigateToString at runtime, but I'm getting the exception:
SystemError: Cannot set unknown member
'{clr-namespace:WebBrowserHelper;Assembly=helpers}WebBrowserHelper.Body'.
I have a working version of this in a C# assembly, but I would like to know how it can be done using IronPython.
My ns declaration looks like this:
xmlns:helpers="clr-namespace:WebBrowserHelper;Assembly=helpers"
and it's used like this:
<WebBrowser helpers:WebBrowserHelper.Body="{Binding html}" />
This is the IronPython code:
import clr
clr.AddReference("System.Windows")
clr.AddReference("WindowsBase")
clr.AddReference("PresentationCore")
clr.AddReference("PresentationFramework")
from System.Windows import (
DependencyObject, DependencyProperty
)
class WebBrowserHelper(DependencyObject):
_Body = None
def __new__(cls):
if notWebBrowserHelper._Body:
WebBrowserHelper._Body = DependencyProperty.RegisterAttached(
"Body", clr.GetClrType(str), clr.GetClrType(WebBrowserHelper), PropertyMetadata(WebBrowserHelper.OnBodyChanged))
return DependencyObject.__new__(cls)
def getBody(self):
return self.GetValue(WebBrowserHelper._Body)
def setBody(self, value):
self.SetValue(WebBrowserHelper._Body, value)
#staticmethod
def OnBodyChanged(dependancyObject, eventArgs):
dependancyObject.NavigateToString(eventArgs.NewValue)
Body = property(getBody, setBody)
Related
Is it possible to make an application in F# that uses WPF with a classic code behind? I know it works perfect with MVVM and no code behind, but I need to implement an interface on a UserControl. Is that possible with F#?
To help a bit, here is the code I want to translate from C# to F#
public class Test : UserControl, IContent {
public void InitializeComponents() {
// Do the initialization magic
}
public Test() {
}
public void OnFragmentNavigation(FragmentNavigationEventArgs e) {
this.DataContext = new { description = "Hallo Welt :)" };
}
public void OnNavigatedFrom(NavigationEventArgs e) {
}
public void OnNavigatedTo(NavigationEventArgs e){
}
public void OnNavigatingFrom(NavigatingCancelEventArgs e) {
}
}
And this is the markup
<UserControl xmlns="http://schemas.microsoft.com/netfx/2007/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Test">
<TextBlock Text="{Binding description}"></TextBlock>
</UserControl>
It really depends on what you mean by "classic code behind". As mentioned by Petr, F# does not have partial classes (and there is also no editor support), so you won't get the same experience (when you are accessing elements or adding events). But you can certainly build a WPF application that uses the same programming model.
One way to get something that is very close to standard code behind is to define a class, associated with each xaml file, that looks something like this:
type SomeComponent() =
let uri = System.Uri("/AppName;component/SomeComponent.xaml", UriKind.Relative)
let ctl = Application.LoadComponent(uri) :?> UserControl
let (?) (this : Control) (prop : string) : 'T =
this.FindName(prop) :?> 'T
let okBtn : Button = ctl?OkButton
do okBtn.Click.Add(fun _ -> (* .. whatever *) )
This loads the XAML content and then uses the dynamic lookup operator to find all the UI elements (which you'd get for free in C#). A nicer F# solution is to use FsXaml, which has a XAML type provider (but sadly, not much documentation).
I found a solution, I just make it short, here is the code:
namespace Testns
open System.Windows.Controls
open FirstFloor.ModernUI.Windows
open Microsoft.FSharp.Core
open System
type TestUserControl() =
inherit UserControl()
interface IContent with
member x.OnFragmentNavigation(e: Navigation.FragmentNavigationEventArgs): unit =
let vm = ViewModel("Hallo Welt :)")
base.DataContext <- vm
()
member x.OnNavigatedFrom(e: Navigation.NavigationEventArgs): unit = ()
member x.OnNavigatedTo(e: Navigation.NavigationEventArgs): unit = ()
member x.OnNavigatingFrom(e: Navigation.NavigatingCancelEventArgs): unit = ()
And the markup
<local:TestUserControl xmlns="http://schemas.microsoft.com/netfx/2007/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Testns;assembly=App">
<TextBlock Text="{Binding description}"></TextBlock>
</local:TestUserControl>
This is not the answer for the actual question, it just works for my use case. So feel free to answer it :)
Probably no. As far as I know F# doesn't support partial classes.
But you can use F# XAML type provider as described here: http://www.mindscapehq.com/blog/index.php/2012/04/29/using-wpf-elements-from-f/
I'm having a problem getting the OnLoad event called from an IronPython script that uses WinForms, I've reduced the problem to the smallest possible reproduction case:
from clr import AddReference, accepts, returns, Self
AddReference("System.Windows.Forms")
AddReference("System.Drawing")
import System
from System.Windows.Forms import *
from System.ComponentModel import *
from System.Drawing import *
class DCCForms: # namespace
class Form1(System.Windows.Forms.Form):
def __init__(self):
self.InitializeComponent()
#returns(None)
def InitializeComponent(self):
self.Name = 'Form1'
self.Text = 'DCC'
self.Load += self._Form1_Load
self.ResumeLayout(False)
self.PerformLayout()
#accepts(Self(), System.Object, System.EventArgs)
#returns(None)
def _Form1_Load(self, sender, e):
pass
class WindowsApplication10: # namespace
#staticmethod
def RealEntryPoint():
Application.EnableVisualStyles()
Application.Run(DCCForms.Form1())
WindowsApplication10.RealEntryPoint();
When run, I get the error 'TypeError: Object is not callable.' for the line self.Load += self._Form1_Load. I've also gotten the same error when trying to set a textboxchanged, however adding an OnClick handler to a button works. The code written above was modified from an IronPython 1.1.2 script generated from the IronPython studio sample from the Visual Studio 2008 SDK. However, I'm running it on Iron Python 2.6.1 from inside a PyDev instance.
If I remove the Load statement, the form is generated and responds to OnClick events if specified.
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.
i have a winforms project, and i created a class on assembly A that inherits from System.Windows.Forms.Form to serve as a base class for various forms on my project, the base class is something like:
public partial class DataForm<T> : Form where T : class
{
T currentRecord;
protected T CurrentRecord
{
get
{
return currentRecord;
}
set
{
currentRecord = value;
CurrentRecordChanged();
}
}
}
Now, when i create a form on assembly B that inherits from my DataForm the designer won't load, but if i compile it the app runs fine.
The form looks like:
public partial class SomeTableWindow : DataForm<SomeTable>
{
public SomeTableWindow ()
{
InitializeComponent();
}
}
The error I'm getting is:
The designer could not be shown for this file because none of the classes within it can be designed.
The designer inspected the following classes in the file: CultivosWindow --- The base
class 'TripleH.Erp.Controls.DataForm' could not be loaded. Ensure the assembly has
been referenced and that all projects have been built.
Instances of this error (1)
1. Hide Call Stack
at System.ComponentModel.Design.Serialization.CodeDomDesignerLoader.EnsureDocument(IDesignerSerializationManager manager)
at System.ComponentModel.Design.Serialization.CodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager manager)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager serializationManager)
at System.ComponentModel.Design.Serialization.BasicDesignerLoader.BeginLoad(IDesignerLoaderHost host)
Is this a bug on the designer?, Am I doing something wrong? Is there some workaround this?
Thank you for your help
It's a known limitation. Basically you can work around this by declaring another class that inherits from the generic class.
For instance:
class Generic<T> : UserControl
{
}
then
class GenericInt : Generic<int> { }
then use GenericInt instead of Generic. SUcks I know.
I have a Silverlight application which has two different XAPs - an InitialXAP which is loaded statically by the HTML page and a DynamicXAP which is loaded from code within the initial XAP. The DynamicXAP is loaded with code similar to this:
var asm = LoadAssemblyFromXap(stream, "DLLName");
// LoadAssemblyFromXAP will load the DynamicXAP as a file stream,
// unpack it and load DLLName as a dll.
var controllerType = asm.GetType("ClassNameToInstantiate_InsideAsm");
var constructor = controllerType.GetConstructor(Type.EmptyTypes);
return constructor.Invoke(null);
I have a class which uses reflection (specifically FieldInfo.GetValue) to do data binding. This class is defined in the InitialXAP. If I try to use this class in the DynamicXAP, I get an error:
Message: Unhandled Error in Silverlight Application System.FieldAccessException: Class.In.DynamicXAP.Which.Uses.The.Reflection.Class.In.InitialXAP
at System.Reflection.RtFieldInfo.PerformVisibilityCheckOnField(IntPtr field, Object target, IntPtr declaringType, FieldAttributes attr, UInt32 invocationFlags)
at System.Reflection.RtFieldInfo.InternalGetValue(Object obj, Boolean doVisibilityCheck, Boolean doCheckConsistency)
at System.Reflection.RtFieldInfo.InternalGetValue(Object obj, Boolean doVisibilityCheck)
at System.Reflection.RtFieldInfo.GetValue(Object obj)
I can get around this error by creating a subclass of the class using reflection and overriding the method using reflection like so:
public class InitialXAP.ClassUsingReflection {
public virtual object GetValue()
{
return fieldInfo.GetValue(parent);
}
}
public class ClassUsingReflection : InitialXAP.ClassUsingReflection {
public override object GetValue()
{
return fieldInfo.GetValue(parent);
}
}
But I would prefer to avoid this duplication by allowing reflection from the InitialXAP in the DynamicXAP. Any ideas on what I can do?
Although there is a learning curve, I would look at Silverlight MEF or Prism (both are together at last in the latest Prism 4 Beta). They both support dynamic loading of modules and enforce good patterns for reuse and separate/team development.
InitialXAP.ClassUsingReflection...
Note the duplicate isn't part of the inital xap namespace (ClassUsingReflection), and may be imported.
Notice GetVisible - as in not visible to Dynamic xap...
Just leave the duplicate (take away base class obviously) and try.