When I set the MonoTouch.Dialog background color to uiclear(transparent), it throw an exception, why? and How to set it to transparent.
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object MyDialogViewController.LoadView () [0x00016] in MyDialogViewController.cs: ParentViewController.View.BackgroundColor = UIColor.Clear
public class MyDialogViewController: DialogViewController
{
public MyDialogViewController (RootElement root) : base (root)
{
}
public override void LoadView()
{
base.LoadView ();
this.TableView.BackgroundColor = UIColor.Clear;
ParentViewController.View.BackgroundColor = UIColor.Clear;
}
}
public void xxxxx(){
var menu = new RootElement(""){
new Section ("Demo"){
new EntryElement("Name", "",""),
},
};
var menuDVC = new MyDialogViewController (menu) {
Autorotate = true
};
this.View.AddSubview(menuDVC.View);
}
The NullReferenceException most likely occurs because ParentViewController is null.
Depending on how your MyDialogViewController is showed this might be due to using the wrong property and a recent, iOS5, change:
Prior to iOS 5.0, if a view did not have a parent view controller and was being presented, the presenting view controller would be returned. On iOS 5, this behavior no longer occurs. Instead, use the presentingViewController property to access the presenting view controller.
However if the MyDialogViewController is the window's RootViewController then it's normal for those properties to be null. In this case simply using UIColor.Clear on the TableView get me a black background (I had nothing there) so it should be enough for MT.D part. If you have a parent then you can try to set it's background color to clear (if needed) before displaying your MyDialogViewController.
Related
I have a problem with running Storyboard for dynamically created UserControl in .net WPF.
These are examples of my classes:
class EventsPage {
// ...
public void AddEvent(Event #event) {
var eventUC = new EventUserContrl(#event);
eventUC.ExpandCollapseAnimation += ExpandCollapseAnimation;
EventsStackPanel.Children.Add(eventUC);
}
private ExpandCollapseAnimation(EventUserControl eventUC, double height, double time) {
// Create frames using custom functions for creating them.
var frames = new DoubleKeyFrameCollection() {
StoryBoardsBuilder.CreateEasingDoubleKeyFrame(0.0, eventUC.ActualHeight),
StoryBoardsBuilder.CreateEasingDoubleKeyFrame(time, destinationHeight)
};
// Create Animation.
var heightSizeAnimation= StoryBoardsBuilder.BuildDoubleAnimationUsingKeyFrames(
FillBehavior.Stop, frames, eventUC.Name, new PropertyPath("Height"));
// Create StoryBoard.
var storyboard = new Storyboard();
// Add Animations into StoryBoard.
storyboard.Children.Add(heightSizeAnimation);
// Create final function.
storyboard.Completed += (sender, e) => {
eventUC.Height = destinationHeight;
};
// Run animation.
storyboard.Begin(this);
}
// ...
}
And after launching it, at storyboard.Begin(this), an exception is shown:
System.InvalidOperationException: „Name „” cannot be found in the namespace „ProjectName.Pages.EventsPage ”.”
I did something like this but for manually placed user controls in page, and it works, but this won't.
This is StoryBuilder code:
public static EasingDoubleKeyFrame CreateEasingDoubleKeyFrame(
double frameTimeInSeconds,
double value) {
// Create double key frame.
return new EasingDoubleKeyFrame() {
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(frameTimeInSeconds)),
Value = value
};
}
public static DoubleAnimationUsingKeyFrames BuildDoubleAnimationUsingKeyFrames(
FillBehavior fillBehavior,
DoubleKeyFrameCollection keyFrames,
string targetName,
PropertyPath targetProperty) {
// Create animation.
var animation = new DoubleAnimationUsingKeyFrames();
// Set animation end behavior.
animation.FillBehavior = fillBehavior;
// Set animation frames.
animation.KeyFrames = keyFrames;
// Set animation target object.
Storyboard.SetTargetName(animation, targetName);
// Set animation target property.
Storyboard.SetTargetProperty(animation, targetProperty);
return animation;
}
It looks like you are setting the target of the Storyboard wrong.
Storyboard.SetTargetName expects a registered element name.
You have two options: either use Storyboard.SetTarget or register the control's name and use Storyboard.SetTargetName.
Solution 1: Storyboard.SetTarget (recommended)
The recommended approach is to use Storyboard.SetTarget, when creating the animation in C# (instead of the more convenient XAML). Storyboard.SetTargetName expects an element within a XAML namescope, having the FrameworkElement.Name set using the X:Name directive. Using Storyboard.SetTarget eliminates the requirement of the target element to be registered within a name scope.
StoryBoardBuilder.cs
class StoryBoardBuilder
{
public static DoubleAnimationUsingKeyFrames BuildDoubleAnimationUsingKeyFrames(
FillBehavior fillBehavior,
DoubleKeyFrameCollection keyFrames,
DependencyObject target,
PropertyPath targetProperty)
{
// Create animation.
var animation = new DoubleAnimationUsingKeyFrames();
// Set animation end behavior.
animation.FillBehavior = fillBehavior;
// Set animation frames.
animation.KeyFrames = keyFrames;
// Set animation target object.
Storyboard.SetTarget(animation, target);
// Set animation target property.
Storyboard.SetTargetProperty(animation, targetProperty);
return animation;
}
}
Example
class EventsPage
{
// ...
public void AddEvent(Event #event) {
var eventUC = new EventUserContrl(#event);
eventUC.ExpandCollapseAnimation += ExpandCollapseAnimation;
}
private ExpandCollapseAnimation(EventUserControl eventUC, double height, double time)
{
...
// Create Animation.
var heightSizeAnimation= StoryBoardBuilder.BuildDoubleAnimationUsingKeyFrames(
FillBehavior.Stop,
frames,
eventUC,
new PropertyPath("Height"));
...
}
// ...
}
Solution 2: Storyboard.SetTargetName
Storyboard.SetTargetName expects a named FrameworkElement as animation target. The name of this element must be registered within the current XAML name scope. This is done automatically when naming elements in XAML using the x:Name directive.
Since you have decided to create the elements in C# you have to register the element name manually. In case there is no active name scope, you also have to create a new name scope manually.
The easiest to get access to an existing name scope is the reference of a named XAML element. On this element you call FrameworkElement.RegisterName to register an element. See Microsoft Docs: Targeting Framework Elements, Framework Content Elements, and Freezables to learn more.
StoryBoardBuilder.cs
class StoryBoardBuilder
{
public static DoubleAnimationUsingKeyFrames BuildDoubleAnimationUsingKeyFrames(
FillBehavior fillBehavior,
DoubleKeyFrameCollection keyFrames,
string targetName,
PropertyPath targetProperty)
{
// Create animation.
var animation = new DoubleAnimationUsingKeyFrames();
// Set animation end behavior.
animation.FillBehavior = fillBehavior;
// Set animation frames.
animation.KeyFrames = keyFrames;
// Set animation target object.
Storyboard.SetTargetName(animation, targetName);
// Set animation target property.
Storyboard.SetTargetProperty(animation, targetProperty);
return animation;
}
}
Example
class EventsPage
{
// ...
public void AddEvent(Event #event)
{
var eventUC = new EventUserContrl(#event);
// Name the element
eventUC.Name = "MyEventUserContrl";
// Register the element name in the current name scope manually
// using an existing named element
EventsStackPanel.RegisterName(eventUC.Name, eventUC);
EventsStackPanel.Children.Add(eventUC);
eventUC.ExpandCollapseAnimation += ExpandCollapseAnimation;
}
private ExpandCollapseAnimation(EventUserControl eventUC, double height, double time)
{
...
// Create Animation.
var heightSizeAnimation= StoryBoardBuilder.BuildDoubleAnimationUsingKeyFrames(
FillBehavior.Stop,
frames,
eventUC.Name,
new PropertyPath("Height"));
...
}
// ...
}
I am working on a project "CoManga" and I wanted to add advertisements in it. Implementing ads on UWP seemed straight forward, like Android and iOS. However, I'm stuck now.
Anyways, I followed this tutorial by James Montemagno and added everything. I even see the test advertisements, which is great. However, when I try to move away from that page (when I press "BACK Button") and go to previous page, I get an error.
This is the error :
Setting up AdControlView in UWP throws System.InvalidOperationException: 'Cannot assign a native control without an Element; Renderer unbound and/or disposed. Please consult Xamarin.Forms renderers for reference implementation of OnElementChanged.'.
It is thrown at line number 50, where I set the SetNativeControl(adView);. I've commented it out right now, but as soon as I un-comment it, I see this error.
Can someone help me out here with this.
Setting up AdControlView in UWP throws System.InvalidOperationException: 'Cannot assign a native control without an Element; Renderer unbound and/or disposed. Please consult Xamarin.Forms renderers for reference implementation of OnElementChanged.
The reason is that xamarin Element has released but SetNativeControl invoked again cause the native control can't find the matched xamarin Element when page going back. So you could set a flag (isRegist) to record the registed ad.
public class AdViewRenderer : ViewRenderer<AdControlView, AdControl>
{
string bannerId = "test";
AdControl adView;
string applicationID = "3f83fe91-d6be-434d-a0ae-7351c5a997f1";
bool isRegist = false;
protected override void OnElementChanged(ElementChangedEventArgs<AdControlView> e)
{
base.OnElementChanged(e);
if (Control == null && isRegist != true)
{
CreateNativeAdControl();
SetNativeControl(adView);
isRegist = true;
}
}
private void CreateNativeAdControl()
{
if (adView != null)
return;
var width = 300;
var height = 50;
if (AnalyticsInfo.VersionInfo.DeviceFamily == "Windows.Desktop")
{
width = 728;
height = 90;
}
// Setup your BannerView, review AdSizeCons class for more Ad sizes.
adView = new AdControl
{
ApplicationId = applicationID,
AdUnitId = bannerId,
HorizontalAlignment = Windows.UI.Xaml.HorizontalAlignment.Center,
VerticalAlignment = Windows.UI.Xaml.VerticalAlignment.Bottom,
Height = height,
Width = width
};
}
}
I need to get the DataContext of the View set by using ContentSource property of the ModernWindow, Could you please help.I am using MVVM framework with Modern UI. The ViewModel code from where I need to show another window is as follows,
public void ShowPrompt()
{
this.PromptWindow = ObjectFactory.GetInstance<IPromptWindowViewModel>().Window as ModernWindow;
this.PromptWindow.Owner = Application.Current.MainWindow;
this.PWPMainViewModel.PromptWindowsCollection.Add(this.PromptWindow);
// Here I need to get the DataContext of PromptWindow's Content
this.PromptWindow.Show();
}
I did some debugging and found that by inherting IContent interface from ModernUI in the 'OnNavigatedTo' event
public void OnNavigatedTo(FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs e)
{
IPWPMainViewModel pwpMainViewModel = ObjectFactory.GetInstance<IPWPMainViewModel>();
pwpMainViewModel.PromptMainsCollection.Add(new ContentControl { Content = e.Content });
IPromptMainViewModel promptMainViewModel = ((UserControl)e.Content).DataContext as IPromptMainViewModel;
}
Here I am able to get the DataContext of the ModernWindow's Content i.e. of type 'IPromptMainViewModel' but here its very difficult to map/load the views into this ModernWindow as there are multiple instances of views, but I would like to do it in the ViewModel where 'ShowPrompt()' is present as there the Model will be associated with the View correctly so I can map there the views easily.
Thank you.
To get this done, I set the Content of the ModernWindow by myself (as shown in below code in a method in a ViewModel) without using the ContentSource DependencyProperty, If we use the ContentSource property it will be set for a ModernFrame type by the ModernWindow itself creating its Content instance after Navigation to that View completes in some method in ModernFrame class from ModernUI for WPF by using ModernFrame's Source DependencyProperty.
public void ShowPrompt()
{
this.PromptWindow = ObjectFactory.GetInstance<IPromptWindowViewModel>().Window as ModernWindow;
this.PromptWindow.Title = string.Concat("Control ", this.PromptOriginsEntity.PromptOriginsIdentity);
this.PromptWindow.Tag = this.PromptOriginsEntity.PromptOriginsIdentity;
this.PromptWindow.Owner = Application.Current.MainWindow;
// Store Window object in PromptWindowsCollection
this.PWPMainViewModel.PromptWindowsCollection.Add(this.PromptWindow);
this.PromptWindow.Show(); // inorder to retrieve the ModernFrame the ModernWindow is to be shown first
ModernFrame frameContent = (ModernFrame)this.PromptWindow.Template.FindName("ContentFrame", this.PromptWindow);
UserControl userControl = new UserControl { Content = GetView<IPromptViewModel>(), Tag = this.PromptOriginsEntity.PromptOriginsIdentity };
frameContent.Content = userControl;
this.PWPMainViewModel.PromptsCollection.Add(userControl);
IPromptViewModel promptViewModel = (IPromptViewModel)((IView)userControl.Content).DataContext;
promptViewModel.PromptEntity.Identity = this.PromptOriginsEntity.PromptOriginsIdentity;
}
I've uploaded a prototype app at https://wpfmvvmsamples.codeplex.com/SourceControl/latest
Thanks.
I am using the Reflection API for MonoTouch.Dialog. What I want to accomplish is, when the user selects an item from a list, I want the navigation controller to go back. I don't want to force the user to click an item, then click the Back button to go back.
However, when trying to use the OnTap attribute, my method doesn't get executed.
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
var demo = new DemoClass();
var context = new BindingContext(this, demo, "Some Demo");
var controller = new DialogViewController(context.Root);
var navController = new UINavigationController(controller);
window.RootViewController = navController;
window.MakeKeyAndVisible ();
return true;
}
public void EnumSelected()
{
InvokeOnMainThread(() =>
{
new UIAlertView("Dialog", "Enum Selected", null, "OK", null).Show();
});
}
DemoClass
public enum DemoEnum
{
SomeValue,
AnotherValue,
YetAnotherValue
}
public class DemoClass
{
[OnTap("EnumSelected")]
public DemoEnum SomeEnum;
}
I know how to navigate back with the navigation controller, but without the OnTap working, I can't get that far. Am I missing something? Can anybody see where I am going wrong?
In a word, you can't.
Enum's (which results in a new RootController and a bunch of RadioElement's) can't have an OnTap set, unless you do it all by hand.
https://github.com/migueldeicaza/MonoTouch.Dialog/blob/master/MonoTouch.Dialog/Reflect.cs#L337
especially, these bits:
csection.Add (new RadioElement (ca != null ? ca.Caption : MakeCaption (fi.Name)));
element = new RootElement (caption, new RadioGroup (null, selected)) { csection };
There is no trigger added to the RadioElement. You would need to change it to auto-pop the form - which needs a new/changed RadioElement
https://gist.github.com/3569920
(I can't claim this code - it came from #escoz: https://github.com/escoz/MonoMobile.Forms )
So, if you are using the built-in MT.D, you can't do it. If you don't mind maintaining your own branch (or, submit a pull request back, which is what I need to do for a few things), then this is a fairly good way to go.
is it possible to define (not switch) VisualStates in CodeBehind?
I'm creating an Adorner, that draws some rectangles in OnRender. What I'd like to do is to change the Opacity of these Rectangles by it's Property IsMouseOver (say from 0.3 to 0.8).
In any control with a visual tree I'd add some VisualStates and switch those with a DataStateBehavior. How do I do this with an Adorner?
this is entirely possible.
if anyone is interested here is how I did it:
public class MyAdorner: Adorner
{
ctor (...):base(...)
{
...
var storyboard = new Storyboard();
var doubleAnimation = new DoubleAnimation(0.2,new Duration(TimeSpan.Zero));
Storyboard.SetTarget(doubleAnimation,this);
Storyboard.SetTargetProperty(doubleAnimation,new PropertyPath(RectOpacityProperty));
storyboard.Children.Add(doubleAnimation);
var storyboard2 = new Storyboard();
var doubleAnimation2 = new DoubleAnimation(0.5, new Duration(TimeSpan.Zero));
Storyboard.SetTarget(doubleAnimation2, this);
Storyboard.SetTargetProperty(doubleAnimation2, new PropertyPath(RectOpacityProperty));
storyboard2.Children.Add(doubleAnimation2);
var stateGroup = new VisualStateGroup { Name = "MouseOverState" };
stateGroup.States.Add(new VisualState { Name = "MouseOut", Storyboard = storyboard });
stateGroup.States.Add(new VisualState { Name = "MouseOver", Storyboard = storyboard2});
var sgs = VisualStateManager.GetVisualStateGroups(this);
sgs.Add(stateGroup);
var dsb = new DataStateBehavior
{
Value = true,
FalseState = "MouseOut",
TrueState = "MouseOver"
};
BindingOperations.SetBinding(dsb, DataStateBehavior.BindingProperty, new Binding {Source = this, Path = new PropertyPath(IsMouseOverProperty)});
dsb.Attach(this);
}
protected override void OnRender(DrawingContext drawingContext)
{
drawingContext.DrawRectangle(_mouseOverBrush, _pen, _rects[i]); //mouseoverbrush is a Solidcolorbrush
}
public double RectOpacity
{
get { return (double)GetValue(RectOpacityProperty); }
set { SetValue(RectOpacityProperty, value); }
}
public static readonly DependencyProperty RectOpacityProperty =
DependencyProperty.Register("RectOpacity", typeof(double), typeof(XmlNodeWrapperAdorner), new FrameworkPropertyMetadata(0.0,FrameworkPropertyMetadataOptions.AffectsRender,(o, args) =>
{
var adorner = o as MyAdorner;
adorner._mouseOverBrush.Color = Color.FromArgb((byte)((double)args.NewValue * 0xFF), 0xFF, 0xBE, 0x00);
}));
}
pretty straightforward actually.
key points here are:
you cannot set the VisualStateGroups attached property. you have to get the collection and then add your own group
you cannot do new DataStateBehavior{Binding = new Binding(...){...}} as this will assign not bind some value to the property. As Behvior<T> doesn't derive from FrameworkElement you also can't use SetBinding but have to use the BindingOperations class.
for automatic rerendering when the property changes keep in mind to set FrameworkPropertyMetadataOptions.AffectsRender.
Since you're already creating a custom adorner with your own behavior, i would suggest that you override the MouseOver method of the adorner and change the opacity of your rectangles there...
another way would be to listen to your own PropertyChanged event and monitor the change in IsMouseOver, or maybe monitor the MouseMove event...
If you could add States in code, tools such as Blend would have to run all code in all possible configurations to find out what states are present/possible.
So, no, you can't do this in code. It only possible using attributes.
EDIT
I stand corrected but the problem mentioned still remains. This technique is not useful for designers.