How change the signature of method supporting UIViewController and UIView? - c#

public void addStatement(UIButton button , UIViewController/UIView view){
//view.add(otherView);
}
this work with UIViewController but i want to use with UIView as well, how i do for support (UIViewController and UIView) the two ?

You can overload the addStatement method like:
public void addStatement(UIButton button, UIViewController view)
{
view.PresentViewController(navController, true, null);
}
public void addStatement(UIButton button, UIView view)
{
view.add(otherView);
}
The compiler will find the correct one, depending on the passed values.
If you have shared functionality, you can do it similar, but call the overloaded methods from the other ones, like:
public void addStatement(UIButton button, UIViewController view)
{
addStatement(button, view.View)
}
public void addStatement(UIButton button, UIView view)
{
view.add(otherView);
}

You can keep it all in one method
public void addStatement(UIButton button, UIViewController currentView = null, UIView otherview = null)
{
if (otherview != null)
{
// do something with otherview
//return
}
if(currentView != null)
{
// do something with currentView
//return
}
}

Related

Prism RegionAdapter on SfNavigationDrawer

I'm trying to implement a RegionAdapter on the Syncfusion navigation drawer (https://help.syncfusion.com/wpf/navigation-drawer/getting-started).
I saw that it uses a variable (object) ContentView for displaying view so I wrote this :
public class SfNavigationDrawerRegionAdapter : RegionAdapterBase<SfNavigationDrawer>
{
public SfNavigationDrawerRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
{
}
protected override void Adapt(IRegion region, SfNavigationDrawer regionTarget)
{
if (region == null)
{
throw new ArgumentNullException(nameof(region));
}
if (regionTarget == null)
{
throw new ArgumentNullException(nameof(regionTarget));
}
region.Views.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (FrameworkElement view in e.NewItems) {
regionTarget.ContentView = view;
}
}
// in events Remove is never called but Reset is called after a Add action
};
}
protected override IRegion CreateRegion()
{
return new SingleActiveRegion();
}
}
This snippet does the following behavior : (https://i.imgur.com/kHVFfQh.mp4) when I click back on a View already loaded it doesn't change the View in the ContentView (not calling the CollectionChanged).
The expected behavior is that it should display the View.
How can I manage to do that?
Thank you for reading!

Prism. Closing a dialog created with IDialogService

I am trying to use a new IDialogService which was discussed in github issue 1666. A New IDialogService for WPF. I like this new feature but I can't find a solution for one case of using IDialogService in compare with InteractionRequest.
There is a button, pressing on which non-modal dialog is opened. If user press the same button one more time, while dialog still open, dialog close. How this behavior should be implemented in a proper way?
MainWindowViewModel
public class MainWindowViewModel : BindableBase
{
private readonly IDialogService _dialogService;
public DelegateCommand CustomPopupCommand { get; }
public MainWindowViewModel(IDialogService dialogService)
{
_dialogService = dialogService;
CustomPopupCommand = new DelegateCommand(OpenClosePopup);
}
private void OpenClosePopup()
{
// It looks like some additional logic should be implemented here.
// How to save previously opened IDialogAware instance and close it if needed?
_dialogService.Show("CustomPopupView", new DialogParameters("Title=Good Title"), result => { });
}
}
CustomPopupViewModel
public class CustomPopupViewModel : BindableBase, IDialogAware
{
private string _title;
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
public DelegateCommand<object> CloseCommand { get; }
public CustomPopupViewModel()
{
CloseCommand = new DelegateCommand<object>(CloseDialog);
}
public event Action<IDialogResult> RequestClose;
public void OnDialogOpened(IDialogParameters parameters)
{
Title = parameters.GetValue<string>(nameof(Title));
}
public void OnDialogClosed()
{
}
public bool CanCloseDialog()
{
return true;
}
public void RaiseRequestClose(IDialogResult dialogResult)
{
RequestClose?.Invoke(dialogResult);
}
private void CloseDialog(object button)
{
RaiseRequestClose(
new DialogResult(button is ButtonResult buttonResult ? buttonResult : ButtonResult.Cancel));
}
}
I have no idea how can it be implemented in proper way because method IDialogService.Show() fully decoupled from knowing about ViewModel and View. Of course except the name of View.
You can always send an event through the event aggregator, probably you have to pass some id in the dialog parameters to close the right dialog if there's more than one open at a time.
But this feels really clunky, I'd prefer to get an IDisposable from Show/ShowDialog that closes the dialog on Dispose.
public CustomPopupViewModel(IEventAggregator eventAggregator)
{
eventAggregator.GetEvent<CloseDialogEvent>().Subscribe( id => { if (id == _id) CloseMe(); } );
}
public void OnDialogOpened(IDialogParameters parameters)
{
_id = parameters.GetValue<string>("id");
}
_dialogService.Show("CustomPopupView", new DialogParameters("id=12345"), result => { });
_eventAggregator.GetEvent<CloseDialogEvent>().Publish("12345");
I find it simplest to use Prism implementation of the subscriber pattern
I use a class that will be used in the pattern and is communicated:
public class DialogStatus
{
public bool DialogResult { get; set; }
}
In my sample, I show you how I do this using a Login Dialog in WPF using Prism 8.0.0.1909
in the App.cs
protected override void OnInitialized()
{
var login = Container.Resolve<LoginDialog>();
var result = login.ShowDialog();
if (result.HasValue && result.Value)
{
base.OnInitialized();
}
else
{
Application.Current.Shutdown();
}
}
in LoginDialog.cs in my Dialogs folder
public partial class LoginDialog : Window
{
public LoginDialog(IEventAggregator eventAggregator)
{
InitializeComponent();
eventAggregator.GetEvent<CloseDialogWindowEvent>().Subscribe(OnCloseWindow);
}
private void OnCloseWindow(DialogStatus obj)
{
base.DialogResult = obj.DialogResult;
}
}
now anywhere in my code, in a ViewModel of view a custom control's view model, the only thing I need to do is pass the IEventAggregator in in the constructor and save it in a field.
private readonly IEventAggregator _eventAggregator;
public LoginControlViewModel(IAuthenticationService authenticationService
, IConnectFileImporterService connectFileImporterService
, IDialogService dialogService
, IEventAggregator eventAggregator)
{
_eventAggregator= eventAggregator;
// the other code
}
I can now close my dialog, and in this sample return true to falls to my OnInitalize in my App.cs from anywhere by calling
_eventAggregator.GetEvent<CloseDialogWindowEvent>().Publish(new CloseDialogWindowEvent() { DialogResult = true });
or
_eventAggregator.GetEvent<CloseDialogWindowEvent>().Publish(new CloseDialogWindowEvent() { DialogResult = false});
If i understand correctly, you want to close the dailog window programmatically instead of clicking the windows's close button, right? If It is true, maybe I can provide you with a solution. Although this method is not very elegant, it is very simple.
My project use mahapps styles, I want use metrowindow as the dailoghost window. Following prism documentation, I register dialoghost window and usercontrol like this:
containerRegistry.RegisterDialogWindow<DialogHost>(nameof(DialogHost));
containerRegistry.RegisterDialog<UserEdit, UserEditViewModel>(nameof(UserEdit));
The UserEidt is a usercontrol, I place a confirm button and a cancel button in UserEidt, and both button binding DelegateCommand in UserEditViewModel. The question is, how can i close dailogwindow by clicking the cancel button?
Here is my solution, firstly define a IDailogViewModel interface:
public interface IDialogViewModel
{
Action CloseDialogWindow { get; set; }
}
Then UserEditViewModel implement this interface:
public class UserEditViewModel : BindableBase, IDialogAware,IDialogViewModel
{
public DelegateCommand CancelCmd { get; private set; }
public Action CloseDialogWindow { get; set; }
public UserEditViewModel()
{
CancelCmd = new DelegateCommand(CloseDialogWindow)
}
private void CloseDialogWindow()
{
CloseDialogWindow.Invoke();
}
}
Infact, when the dialog window popup, the UserEdit will be dialogWindow's content. So in the dialogwindow's loaded event handler, i can get the UserEdit object by using Window.Content, here is the code:
public partial class DialogHost : MetroWindow, IDialogWindow
{
public DialogHost()
{
InitializeComponent();
}
public IDialogResult Result { get; set; }
private void MetroWindow_Loaded(object sender, RoutedEventArgs e)
{
var dialogVM = (IDialogViewModel)((UserControl)Content).DataContext;
dialogVM.CloseDialogWindow += CloseDialogWindow;
}
void CloseDialogWindow()
{
Close();
}
}
Now,after clicking the cancel button, the dialogwindow will be close.

Block mouse bubble in Xamarin Mac

I have created a custom NSView that i would like to place over the top of the content of a window to block any interaction while all the content is loading. The problem i was having is that i could click through the NSView to the controls below though that has now been fixed. The new problem is that even though i cannot click on the controls, when i move the mouse over text controls, the mouse switches to the I Beam icon.
How do i make the NSView completely block all interaction with everything below it?
The NSView i created is below:
[Register("StupidView")]
public class StupidView : NSView
{
public StupidView()
{
// Init
Initialize();
}
public StupidView(IntPtr handle) : base (handle)
{
// Init
Initialize();
}
[Export("initWithFrame:")]
public StupidView(CGRect frameRect) : base(frameRect) {
// Init
Initialize();
}
private void Initialize()
{
this.AcceptsTouchEvents = true;
this.WantsLayer = true;
this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
}
public override void DrawRect(CGRect dirtyRect)
{
var ctx = NSGraphicsContext.CurrentContext.GraphicsPort;
ctx.SetFillColor(new CGColor(128, 128, 128, 0.7f));
ctx.FillRect(dirtyRect);
}
public override void MouseDown(NSEvent theEvent)
{
if (Hidden)
{
base.MouseDown(theEvent);
}
}
public override void MouseDragged(NSEvent theEvent)
{
if (Hidden)
{
base.MouseDragged(theEvent);
}
}
public override bool AcceptsFirstResponder()
{
return !this.Hidden;
}
public override bool AcceptsFirstMouse(NSEvent theEvent)
{
return !this.Hidden;
}
public override NSView HitTest(CGPoint aPoint)
{
return Hidden ? null : this;
}
}
I had the same problem a few weeks ago, and here is how I could manage this :
First, to prevent user interactions on the superview placed below, I added a transparent button which was there only to catch the mouse click and, if you don't have to do anything, do nothing :
private void Initialize()
{
this.AcceptsTouchEvents = true;
this.WantsLayer = true;
this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
//Add button to prevent user interactions
NSButton buttonToPreventUserInteraction = new NSButton();
buttonToPreventUserInteraction.Bordered = false;
buttonToPreventUserInteraction.Transparent = true;
buttonToPreventUserInteraction.TranslatesAutoresizingMaskIntoConstraints = false;
AddSubview(buttonToPreventUserInteraction);
//If you want to add some constraints on the button, for it to resize and keep the same size of your subview
var dicoViews = new NSMutableDictionary();
dicoViews.Add((NSString)"buttonToPreventUserInteraction", buttonToPreventUserInteraction);
NSLayoutConstraint[] buttonToPreventUserInteractionHorizontalConstraints = NSLayoutConstraint.FromVisualFormat("H:|[buttonToPreventUserInteraction]|", NSLayoutFormatOptions.DirectionLeadingToTrailing, null, dicoViews);
NSLayoutConstraint[] buttonToPreventUserInteractionVerticalConstraints = NSLayoutConstraint.FromVisualFormat("V:|[buttonToPreventUserInteraction]|", NSLayoutFormatOptions.DirectionLeadingToTrailing, null, dicoViews);
AddConstraints(buttonToPreventUserInteractionHorizontalConstraints);
AddConstraints(buttonToPreventUserInteractionVerticalConstraints);
}
For your other problem, which is the mouse cursor changing from the content in your superview placed below, you can add a NSTrackingArea on your subview, and implement the override method "MouseMoved" to change the cursor. You can do something like this :
First Add the NSTrackingArea on your subview (you can put this code in your "Initialize" method)
NSTrackingAreaOptions opts = ((NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.InVisibleRect));
var trackingArea = new NSTrackingArea(new CGRect(0, 0, FittingSize.Width, FittingSize.Height), opts, Self, null);
AddTrackingArea(trackingArea);
And then implement the override method :
public override void MouseMoved(NSEvent theEvent)
{
//You can choose the type of cursor you want to use here
NSCursor.ArrowCursor.Set();
}
This made it for me, hope it will for you too

Caliburn.Micro Calling a viewmodel from UserControl

I have a AppViewModel, that contains a menu on Top of the Window. On the AppViewModel construct, I'm showing a UserControl. In this UserControl I have a button, that calls another viewmodel (UserControl).
The idea is to keep the menu and working on the content of window. So, I have 1 window and 2 UserControls. This is correct?
How can I call another ViewModel from a button that is inside of a UserControl? Or, I have to call it from the Window? But the button it's inside of the UserControl!
My code:
class AppViewModel : Conductor<object>
{
private bool _MenuIsVisible;
public bool MenuIsVisible
{
get { return _MenuIsVisible; }
set
{
if (_MenuIsVisible != value)
{
_MenuIsVisible = value;
NotifyOfPropertyChange(() => MenuIsVisible);
}
}
}
public AppViewModel()
{
MenuIsVisible = true;
_ShowTutorial();
}
private void _ShowTutorial()
{
ActivateItem(new FirstViewModel());
}
}
public class FirstViewModel : Screen
{
protected override void OnActivate()
{
base.OnActivate();
}
}
On the FirstViewModel I have a button that needs to call SecondViewModel.
To navigate from the first ViewModel to the second ViewModel you could have a method in the first ViewModel like this:
public void NavigateToSecond()
{
var conductor = this.Parent as IConductor;
conductor.ActivateItem(new SecondViewModel());
}
The parent refers to the conductor which will take care of navigating for you.

Restrict orientation of view controller when using a static UITableView

I've set up a static UITableView with iOS Designer (IB in Objective-C world). But the orientation is changed despite I want to restrict it.
I've done the following:
In Properties under Simulated Metrics I chose Portrait as Orientation. Than I'm implementing the following functions for my UITableViewController:
public override bool ShouldAutorotate ()
{
return false;
}
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations ()
{
return UIInterfaceOrientationMask.Portrait;
}
public override UIInterfaceOrientation PreferredInterfaceOrientationForPresentation ()
{
return UIInterfaceOrientation.Portrait;
}
GetSupportedInterfaceOrientations is called and I return Portrait but the view is still rotated. What I'm missing?
Edit:
I've used the approach discussed in View Orientation. This works for my view controllers. The static UITableViewController is pushed in this way on the stack:
this.PresentViewController (new UINavigationController(myStaticTableViewController), true, null);
Here the standard implementation of UINavigationController is used. I also tried it with my CustomNavigationController which implements
partial class CustomNavigationController : UINavigationController
{
public CustomNavigationController (IntPtr handle) : base (handle)
{
}
public override bool ShouldAutorotate ()
{
return TopViewController.ShouldAutorotate();
}
public override UIInterfaceOrientation PreferredInterfaceOrientationForPresentation ()
{
return TopViewController.PreferredInterfaceOrientationForPresentation ();
}
}
but I can't do something like this
this.PresentViewController (new CustomNavigationController(myStaticTableViewController), true, null);
because it cannot convert my table view controller to IntPtr. Perhaps that's the reason why it doesn't respect the interface orientation. What solutions do I have?
Seems that I only had to add another constructor as stated in the linked thread. Now my CustoMNavigationController looks like this:
partial class CustomNavigationController : UINavigationController
{
public CustomNavigationController(UIViewController rootViewController) : base(rootViewController)
{
}
public CustomNavigationController (IntPtr handle) : base (handle)
{
}
public override bool ShouldAutorotate ()
{
return TopViewController.ShouldAutorotate();
}
public override UIInterfaceOrientation PreferredInterfaceOrientationForPresentation ()
{
return TopViewController.PreferredInterfaceOrientationForPresentation ();
}
}
Now I can use
this.PresentViewController (new CustomNavigationController(myStaticTableViewController), true, null)
and everything works as expected.

Categories