Anyone tried this scenario before? I'm talking about latest Xamarin forms 2 targetting iOS.
I have a TabbedPage with 4 tabs, when the user looks at the first 2, there should not be a navigationbar - I got this working by setting the NavigationPage.SetHasNavigationBar(this, false) in the constructor of the TabbedPage.
Now by using the ExtendedTabbedPage of Xamarin forms labs, I can hook up to the change tab event called OnCurrentPageChanged() and in here I verify if I'm on one of the last 2 tabs and toggle the NavigationPage.SetHasNavigationBar(this, true).
This actually works great, except for 1 small detail. When the navigation bar gets shown, tab page shifts down and it hides the tabs at the bottom, so the user is unable to switch tabs at that moment.
When I set NavigationPage.SetHasNavigationBar(this, true) in the constructor of the tabbedpage and leave it for all tabs, the result is ok. Meaning I get a navigationheader and tabs visible on each tab.
Ok found a solution myself.
So the original setup was as follows, in the App.xaml.cs I initiated the Navigation through following code
NavigationPage navigationPage = new NavigationPage(new MainPage());
MainPage is a TabbedPage containing several children.
What I did was toggling the NavigationBar of that MainPage when switching from one child page to another
NavigationPage.SetHasNavigationBar(this, false);
NavigationPage.SetHasNavigationBar(this, true);
But that resulted in the child pages not being resized, leaving the tabbar at the bottom of the page invisible when the navigationbar came visible.
So the actual solution is to not set the MainPage as a NavigationPage, but wrap each child in a NavigationPage!
This way each child can have it's own NavigationBar an that keeps the size in reference to the MainPage correct.
So adding each child would be like this
this.Children.Add (new NavigationPage (new DiscoverPage ()){ Title = "Discover" });
Took me a while to figure this out because I normally would add the children inside the actual xaml of the MainPage but that way you can't wrap them in a NavigationPage, hence why I wrapped the MainPage itself inside the NavigationPage!
It could be some bug in computing correct size of tabbed page in NavigationPage when toggling NavagationBar visibility.
In order that I need that tabbed page must be in NavigationPage I came up with the next solution:
public class BaseNavigationPage : NavigationPage
{
public BaseNavigationPage() : base()
{
this.BarTextColor = Color.White;
}
public BaseNavigationPage(Page page) : base(page)
{
this.BarTextColor = Color.White;
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == nameof(CurrentPage))
{
Current = CurrentPage;
}
}
private void CurrentPage_PropertyChanging(object sender, PropertyChangingEventArgs e)
{
if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName && _bounds == Rectangle.Zero)
{
_bounds = CurrentPage.Bounds;
}
}
private void Current_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName)
{
var has = NavigationPage.GetHasNavigationBar(CurrentPage);
if (has)
{
CurrentPage.Layout(_bounds);
_bounds = Rectangle.Zero;
}
else
{
CurrentPage.Layout(this.Bounds);
}
}
}
private Page Current
{
get { return _current; }
set
{
if (_current != null)
{
_current.PropertyChanging -= CurrentPage_PropertyChanging;
_current.PropertyChanged -= Current_PropertyChanged;
}
_current = value;
if (_current != null)
{
_current.PropertyChanging += CurrentPage_PropertyChanging;
_current.PropertyChanged += Current_PropertyChanged;
}
}
}
private Page _current;
private Rectangle _bounds;
}
And use may BaseNavigationPage instead of NavigationPage and all works fine.
I have two Pages and one MainWindow.. I load the Pages in two Frames.. Now I want to execute methods from each other.. How can I do this?
This is Page1.cs:
public partial class Page1 : Page
{
public Method1()
{
doSomething;
}
}
This is Page2.cs:
public partial class Page2 : Page
{
public Method2()
{
doSomethingElse;
}
}
In my MainWindow the following happens:
Frame1.Source = new Uri("/Source/Pages/Page1.xaml", UriKind.RelativeOrAbsolute);
Frame2.Source = new Uri("/Source/Pages/Page2.xaml", UriKind.RelativeOrAbsolute);
Is there any way, to execute Method2 from Page1.cs, and Method1 from Page2.cs?
Regards
One way to do this is through their common parent, the window.
Looking at this (modified accordingly)
public partial class MainWindow : Window
{
public Page1 Page1Ref = null;
public Page1 Page2Ref = null;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Frame1.Source = new Uri("/Source/Pages/Page1.xaml", UriKind.Relative);
Frame1.ContentRendered += Frame1_ContentRendered;
// do the same for the Frame2
}
private void Frame1_ContentRendered(object sender, EventArgs e)
{
var b = Frame1.Content as Page1; // Is now Home.xaml
Page1Ref = b;
if(Page2Ref != null) // because you don't know which of the pages gets rendered first
{
Page2Ref.Page1Ref = Page1Ref; // add the Page1Ref prop to your Page2 class
Page1Ref.Page2Ref = Page2Ref; // here the same
}
}
// do the same for the other page
}
from this question
you should be able to set a reference once a page is loaded to the other page .
Better yet, you might want to let the Pages know of their window parent and access the other page through it. Either way, is bad design, I'm telling you.
Is not a solution to be proud of, you might better look into MVVM, and go with it.
Let me know if it worked for you.
I have used the code from this blog, as below but abridged, and I see the WinForm inside my main window, but the sample text I placed on it in a label is not visible.
[System.Windows.Markup.ContentProperty("Child")]
public class WinFormsHost : HwndHost
{
public WinFormsHost()
{
var form = new ChildForm();
Child = form;
}
private System.Windows.Forms.Form child;
public event EventHandler<ChildChangedEventArgs> ChildChanged;
public System.Windows.Forms.Form Child
{
get { return child; }
set
{
HwndSource ps = PresentationSource.FromVisual(this) as HwndSource;
if (ps != null && ps.Handle != IntPtr.Zero)
{
throw new InvalidOperationException("Cannot set the Child property after the layout is done.");
}
Form oldChild = child;
child = value;
OnChildChanged(oldChild);
}
}
private void CheckChildValidity()
{
if (child == null || child.Handle == IntPtr.Zero)
{
throw new ArgumentNullException("child form cannot be null");
}
}
public Boolean ShowCaption
{
get
{
CheckChildValidity();
return (GetWindowStyle(Child.Handle) & WindowStyles.WS_BORDER) == WindowStyles.WS_CAPTION;
}
set
{
if (child == null)
{
this.ChildChanged += delegate
{
if (value)
{
SetWindowStyle(Child.Handle, GetWindowStyle(Child.Handle) | WindowStyles.WS_CAPTION);
}
else
{
SetWindowStyle(Child.Handle, GetWindowStyle(Child.Handle) & ~WindowStyles.WS_CAPTION);
}
};
}
else
{
if (value)
{
SetWindowStyle(Child.Handle, GetWindowStyle(Child.Handle) | WindowStyles.WS_CAPTION);
}
else
{
SetWindowStyle(Child.Handle, GetWindowStyle(Child.Handle) & ~WindowStyles.WS_CAPTION);
}
}
}
}
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
CheckChildValidity();
HandleRef childHwnd = new HandleRef(Child, child.Handle);
SetWindowStyle(childHwnd.Handle, WindowStyles.WS_CHILD | GetWindowStyle(childHwnd.Handle));
WindowsFormsHost.EnableWindowsFormsInterop();
System.Windows.Forms.Application.EnableVisualStyles();
SetParent(childHwnd.Handle, hwndParent.Handle);
return childHwnd;
}
}
And:
<Window x:Class="WinFormsHost"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
xmlns:cc="clr-namespace:XTime.Shell.WinformsHost"
Title="Hosting Form In WPF">
<cc:WinFormsHost ShowCaption="False">
<wf:Form/>
</cc:WinFormsHost>
</Window>
<cc:WinFormsHost ShowCaption="False">
<wf:Form/>
</cc:WinFormsHost>
Your XAML embeds a System.Windows.Forms.Form object inside the WinFormsHost. Which is what you got, just a blank form with no child controls embedded inside it. It looks like you made an attempt at creating your own in the WinFormsHost constructor, assigning the Child property, but your XAML is overriding it so you are just left with a blank form again.
I put a ChildForm class inside the same namespace:
using System.Windows.Forms;
using System.Drawing;
...
public class ChildForm : System.Windows.Forms.Form {
public ChildForm() {
this.BackColor = Color.FromKnownColor(KnownColor.Window);
var lbl = new Label { Text = "Hello world" };
this.Controls.Add(lbl);
}
}
And updated the XAML to:
<cc:WinFormsHost ShowCaption="False">
<cc:ChildForm/>
</cc:WinFormsHost>
To get:
Set the FormBorderStyle to None to get rid of the border. Etcetera.
Setting the form's TopLevel property to false and Visible property to true is the much simpler way to turn a Form into a child control btw. I left it this way since you hinted you might want to give a Delphi window the same treatment. In which case you might want to go back to your original approach again, creating the child in the form class constructor and just omitting the content assignment in the XAML.
in vb6 i can easily get the value from childwindow to another childwindow.. for example frm1.textbox1.text = frm2.listview.item.selectedindex... how can i do this in wpf?
i have two child window named EmployeProfile and the other one is PrintEmpProfile... in EmployeeProfile window, there is a listview... what i want is if I'm going to click the print button, i can get the value from EmployeeProfile listview....
so far this is what's I've got. this code is inside of PrintEmpProfile
DataTable table = new DataTable("EmpIDNumber");
table.Columns.Add("IDNum", typeof(string));
for (int i = 1; i <= 100; i++)
{
table.Rows.Add(new object[] { EmployeeProfile.????? });
}
i don't know how to get all values from EmployeeProfile listview.
Whenever you open a child window put a reference of the new child window into a collection. I ssume that MyChild is your child window defined in XAML like:
<Window
x:Class="TEST.MyChild"
...
You can define a static list holding your child windows in App.xaml.cs
public partial class App : Application
{
public static List<MyChild> Children
{
get
{
if (null == _Children) _Children = new List<MyChild>();
return _Children;
}
}
private static List<MyChild> _Children;
}
Whenever you open child window add it into this collection like:
MyChild Child = new MyChild();
App.Children.Add(Child);
Child.Show();
You should also remove child from this collection when you close your child window. You can do this in Closed event of the window. Define closed vent in XML:
Closed="MyChild_Closed"
And in code behind:
private void MyChild_Closed(object sender, EventArgs e)
{
// You may check existence in the list first in order to make it safer.
App.Children.Remove(this);
}
Whenever a child window wants to access ListView of other child window then it gets reference of the child from the collection and then call ListView defined in XAML directly.
MyChild ChildReference = App.Children.Where(x => x.Title == "Child Title").FirstOrDefault();
if (null != ChildReference)
{
ChildReference.listview.SelectedIndex = ...
}
You can create Property Of listview .
public ListView EmployeeListView
{
get
{
return IdOfYourListView;
}
set
{
IdOfYourListView = value;
}
}
Now on PrintEmpProfile Create object of EmployeeProfile
EmployeeProfile empf = new EmployeeProfile();
ListView MyListView = empf.EmployeeListView;
I have a user control that I load into a MainWindow at runtime. I cannot get a handle on the containing window from the UserControl.
I have tried this.Parent, but it's always null. Does anyone know how to get a handle to the containing window from a user control in WPF?
Here is how the control is loaded:
private void XMLLogViewer_MenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem application = sender as MenuItem;
string parameter = application.CommandParameter as string;
string controlName = parameter;
if (uxPanel.Children.Count == 0)
{
System.Runtime.Remoting.ObjectHandle instance = Activator.CreateInstance(Assembly.GetExecutingAssembly().FullName, controlName);
UserControl control = instance.Unwrap() as UserControl;
this.LoadControl(control);
}
}
private void LoadControl(UserControl control)
{
if (uxPanel.Children.Count > 0)
{
foreach (UIElement ctrl in uxPanel.Children)
{
if (ctrl.GetType() != control.GetType())
{
this.SetControl(control);
}
}
}
else
{
this.SetControl(control);
}
}
private void SetControl(UserControl control)
{
control.Width = uxPanel.Width;
control.Height = uxPanel.Height;
uxPanel.Children.Add(control);
}
Try using the following:
Window parentWindow = Window.GetWindow(userControlReference);
The GetWindow method will walk the VisualTree for you and locate the window that is hosting your control.
You should run this code after the control has loaded (and not in the Window constructor) to prevent the GetWindow method from returning null. E.g. wire up an event:
this.Loaded += new RoutedEventHandler(UserControl_Loaded);
I'll add my experience. Although using the Loaded event can do the job, I think it may be more suitable to override the OnInitialized method. Loaded occurs after the window is first displayed. OnInitialized gives you chance to make any changes, for example, add controls to the window before it is rendered.
Use VisualTreeHelper.GetParent or the recursive function below to find the parent window.
public static Window FindParentWindow(DependencyObject child)
{
DependencyObject parent= VisualTreeHelper.GetParent(child);
//CHeck if this is the end of the tree
if (parent == null) return null;
Window parentWindow = parent as Window;
if (parentWindow != null)
{
return parentWindow;
}
else
{
//use recursion until it reaches a Window
return FindParentWindow(parent);
}
}
I needed to use the Window.GetWindow(this) method within Loaded event handler. In other words, I used both Ian Oakes' answer in combination with Alex's answer to get a user control's parent.
public MainView()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainView_Loaded);
}
void MainView_Loaded(object sender, RoutedEventArgs e)
{
Window parentWindow = Window.GetWindow(this);
...
}
If you are finding this question and the VisualTreeHelper isn't working for you or working sporadically, you may need to include LogicalTreeHelper in your algorithm.
Here is what I am using:
public static T TryFindParent<T>(DependencyObject current) where T : class
{
DependencyObject parent = VisualTreeHelper.GetParent(current);
if( parent == null )
parent = LogicalTreeHelper.GetParent(current);
if( parent == null )
return null;
if( parent is T )
return parent as T;
else
return TryFindParent<T>(parent);
}
This approach worked for me but it is not as specific as your question:
App.Current.MainWindow
How about this:
DependencyObject parent = ExVisualTreeHelper.FindVisualParent<UserControl>(this);
public static class ExVisualTreeHelper
{
/// <summary>
/// Finds the visual parent.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sender">The sender.</param>
/// <returns></returns>
public static T FindVisualParent<T>(DependencyObject sender) where T : DependencyObject
{
if (sender == null)
{
return (null);
}
else if (VisualTreeHelper.GetParent(sender) is T)
{
return (VisualTreeHelper.GetParent(sender) as T);
}
else
{
DependencyObject parent = VisualTreeHelper.GetParent(sender);
return (FindVisualParent<T>(parent));
}
}
}
I've found that the parent of a UserControl is always null in the constructor, but in any event handlers the parent is set correctly. I guess it must have something to do with the way the control tree is loaded. So to get around this you can just get the parent in the controls Loaded event.
For an example checkout this question WPF User Control's DataContext is Null
Another way:
var main = App.Current.MainWindow as MainWindow;
It's working for me:
DependencyObject GetTopLevelControl(DependencyObject control)
{
DependencyObject tmp = control;
DependencyObject parent = null;
while((tmp = VisualTreeHelper.GetParent(tmp)) != null)
{
parent = tmp;
}
return parent;
}
This didn't work for me, as it went too far up the tree, and got the absolute root window for the entire application:
Window parentWindow = Window.GetWindow(userControlReference);
However, this worked to get the immediate window:
DependencyObject parent = uiElement;
int avoidInfiniteLoop = 0;
while ((parent is Window)==false)
{
parent = VisualTreeHelper.GetParent(parent);
avoidInfiniteLoop++;
if (avoidInfiniteLoop == 1000)
{
// Something is wrong - we could not find the parent window.
break;
}
}
Window window = parent as Window;
window.DragMove();
If you just want to get a specific parent, not only the window, a specific parent in the tree structure, and also not using recursion, or hard break loop counters, you can use the following:
public static T FindParent<T>(DependencyObject current)
where T : class
{
var dependency = current;
while((dependency = VisualTreeHelper.GetParent(dependency) ?? LogicalTreeHelper.GetParent(dependency)) != null
&& !(dependency is T)) { }
return dependency as T;
}
Just don't put this call in a constructor (since the Parent property is not yet initialized). Add it in the loading event handler, or in other parts of your application.
DependencyObject parent = ExVisualTreeHelper.FindVisualParent<UserControl>(this);
DependencyObject GetTopParent(DependencyObject current)
{
while (VisualTreeHelper.GetParent(current) != null)
{
current = VisualTreeHelper.GetParent(current);
}
return current;
}
DependencyObject parent = GetTopParent(thisUserControl);
The Window.GetWindow(userControl) will return the actual window only after the window was initialized (InitializeComponent() method finished).
This means, that if your user control is initialized together with its window (for instance you put your user control into the window's xaml file), then on the user control's OnInitialized event you will not get the window (it will be null), cause in that case the user control's OnInitialized event fires before the window is initialized.
This also means that if your user control is initialized after its window, then you can get the window already in the user control's constructor.
Gold plated edition of the above (I need a generic function which can infer a Window within the context of a MarkupExtension:-
public sealed class MyExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider) =>
new MyWrapper(ResolveRootObject(serviceProvider));
object ResolveRootObject(IServiceProvider serviceProvider) =>
GetService<IRootObjectProvider>(serviceProvider).RootObject;
}
class MyWrapper
{
object _rootObject;
Window OwnerWindow() => WindowFromRootObject(_rootObject);
static Window WindowFromRootObject(object root) =>
(root as Window) ?? VisualParent<Window>((DependencyObject)root);
static T VisualParent<T>(DependencyObject node) where T : class
{
if (node == null)
throw new InvalidOperationException("Could not locate a parent " + typeof(T).Name);
var target = node as T;
if (target != null)
return target;
return VisualParent<T>(VisualTreeHelper.GetParent(node));
}
}
MyWrapper.Owner() will correctly infer a Window on the following basis:
the root Window by walking the visual tree (if used in the context of a UserControl)
the window within which it is used (if it is used in the context of a Window's markup)
Different approaches and different strategies. In my case I could not find the window of my dialog either through using VisualTreeHelper or extension methods from Telerik to find parent of given type. Instead, I found my my dialog view which accepts custom injection of contents using Application.Current.Windows.
public Window GetCurrentWindowOfType<TWindowType>(){
return Application.Current.Windows.OfType<TWindowType>().FirstOrDefault() as Window;
}