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;
}
Related
I have a search window, which looks like following:
The part with Condition and Options is a ContentControl with several DataTemplates, which contain different filter form for specific field (eg. datetime picker etc.).
I'd like specific control in the DataTemplate to be focused after opening the window (this is the X problem if someone asked)
I'm doing that in the following way:
public FindWindow(FindModel model)
{
InitializeComponent();
this.viewModel = Dependencies.Container.Instance.Resolve<FindWindowViewModel>(new ParameterOverride("access", this), new ParameterOverride("model", model));
DataContext = viewModel;
FocusInput();
}
FocusInput does the following:
public static FrameworkElement GetControlByName(DependencyObject parent, string name)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < count; ++i)
{
var child = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
if (child != null)
{
if (child.Name == name)
{
return child;
}
var descendantFromName = GetControlByName(child, name);
if (descendantFromName != null)
{
return descendantFromName;
}
}
}
return null;
}
public void FocusInput()
{
Dispatcher.Invoke(DispatcherPriority.ContextIdle, new Action(() =>
{
var obj = GetControlByName(filterContainer, "input");
if (obj != null && obj is Control ctl)
ctl.Focus();
}));
}
When it runs in the ctor, FindWindow gets null obj (despite ContentControl having Content set). However, when you click "Test" button, which simply runs FocusControl, the latter in turn finds required control and focuses it.
The question is: how to capture moment, when ContentControl finishes instantiating DataTemplate, such that I can capture required control? (Problem Y)
I'll be grateful for solution to either problem X or Y (which is my attempted solution).
Try to call FocusInput() once the window or ContentControl has been loaded:
public FindWindow(FindModel model)
{
InitializeComponent();
this.viewModel = Dependencies.Container.Instance.Resolve<FindWindowViewModel>(new ParameterOverride("access", this), new ParameterOverride("model", model));
DataContext = viewModel;
Loaded += (s, e) => FocusInput();
}
Maybe, a better solution would be behavior. It is inherited from
using System.Windows.Interactivity;
And is very similar to the previous answer, and is reusable
public class FocusBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
if(AssociatedObject!=null)
{
//LOADED EVENT SUBSCRIBE
AssociatedObject.Loaded += //YOUR FOCUS METHOD;
}
}
protected override void OnDetaching()
{
base.OnDetaching();
//LOADED EVENT UNSUBSCRIBE
AssociatedObject.Loaded -= //YOUR FOCUS METHOD;
}
}
and then attach it to your element in XAML:
<TextBox
x:Name="MainText">
<i:Interaction.Behaviors>
<behaviors:FocusBehavior />
</i:Interaction.Behaviors>
</TextBox>
i'm coding a c# application using WPF
i have a main Window which contain a Grid named " SelectionGrid ". this grid will contain Control User, my problem is that i want to modify ( add/delete) Control User in that grid from a USER CONTROL itself
for example:
SelectionGrid host the User Control " Menu" in this menu there a button, i want from this button to remove the Menu User Control and add another User Control in this SelectionGrid
main window code :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
UserControl usc = new Menu();
SelectionGrid.Children.Add(usc);
}}
Menu User Control code :
public partial class Menu : UserControl
{
public Menu()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// want to add Another User Control in SelectionGrid
}
Firstly,put your userControl in some container control like Grid.Then you can easily access and modify the grid from the usercontrol as follows:
var parent = (Grid)this.Parent;
///do what you want to do with parent
A bit of knowledge-sharing : The below code can be used to access the parent of controls like Page,UserControl :
public static T FindParent(DependencyObject child)
{
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null)
return null;
T parent = parentObject as T;
if (parent != null)
return parent;
else
return FindParent<T>(parentObject);
}
private void test()
{
ControlTypeHere parent = FindParent<ControlTypeHere>(this);
Hope this helps :)
I am using custom panel from my custom Items control( DisplayPanelControl which is derived from List box) the style is some thing similar to following XAML
<Style x:Key="ContainerStyle" TargetType="{x:Type local:DisplayPanelControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<local:CustomePanel Background="AliceBlue" IsItemsHost="True">
</local:CustomePanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
the CustomePanel has a property Edgblending. I want to set this property through my Items control, so I have overridden OnApplyTemplate() method and used VisualTreeHelper to find my customPanel as set the desired property.
I want to ask if there is a better solution for setting properties on ItemsPanel through Itemscontrol?
This is one of the possible work around which will work in under 2 situation, Have used the method on Items controls OnApplyTemplate() method.
When we specify Panel in itemsControls template by setting IsItemsHost property on the Panel
When we set the Items panel via "ItemsPanelTemplate" markup.
Have come to this workaround by the explanation given by Ian Griffiths Find Control Inside ListBox? answer.
private T GetItemsPanel<T>(ItemsControl itemsControl) where T : Panel
{
T _Panel = UIHelper.FindVisualChild<T>(itemsControl);
if (_Panel == null)
{
ItemsPresenter itemsPresenter = UIHelper.FindVisualChild<ItemsPresenter>(itemsControl);
if (itemsPresenter != null)
{
itemsPresenter.ApplyTemplate();
_Panel = VisualTreeHelper.GetChild(itemsPresenter, 0) as T;
}
}
return _Panel;
}
The implementation for UiHelper class is nothing but finding the object in visual tree and implementation is as below(I have copied this as well from some blog post but cant remember to find the link)
public static class UIHelper
{
/// <summary>
/// Finds a parent of a given item on the visual tree.
/// </summary>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="child">A direct or indirect child of the queried item.</param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found, a null reference is being returned.</returns>
public static T FindVisualParent<T>(DependencyObject child)
where T : DependencyObject
{
// get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
// we’ve reached the end of the tree
if (parentObject == null) return null;
// check if the parent matches the type we’re looking for
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
// use recursion to proceed with next level
return FindVisualParent<T>(parentObject);
}
}
public static T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = FindVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
}
I'm attempting to implement a custom search dialog in a WPF program. The Parent window is a ListView that is bound to an Observable Collection.
I've made a new window with the search form and it is initialized like so:
searchForm sf = new searchForm(_QPCollection);
sf.Owner = this;
sf.Show();
and I have this function I am trying to call (in the Owner window):
public void selectIndex(int index)
{
ListViewItem toSelect = listView1.Items[index] as ListViewItem;
toSelect.Focus();
}
Then in the Child window (searchForm) attempting to call selectIndex like so:
public void SearchJob_Click(object sender, RoutedEventArgs e)
{
if (sJob.Text == "" || sJob.Text == null) { return; }
for (int i = findCount; i < _QPCollectionSearch.Count; i++)
{
if (i == _QPCollectionSearch.Count - 1) { i = 0; }
if (_QPCollectionSearch[i].jobNumAndFlow.IndexOf(sJob.Text) > -1)
{
findCount = i;
Owner.selectIndex(i);
}
}
}
I get the error: System.Windows.Window does not contain a definition for "selectIndex".
The _QPCollection is the observable collection that the search will loop through. I have the search logic working, but I cannot seem Focus() the index of the ListView in the Parent Window.
My first thought was to have a public function that I could pass the index to and it would do the focus, but I cannot seem to find a way to call function from the Child Window that are in the Parent Window.
Am I approaching this completely wrong? This answer seems to be for WinForms but I am sure there is a way to access the Parent Window and its public functions/properties in WPF.
A cleaner way of handling that scenario would be for your searchForm to raise an event. The parent window could listen for that event and set the focus on its own list view:
public class searchForm
{
public event EventHandler<SearchEventArgs> SearchResultSelected = delegate { };
}
public class SearchEventArgs : EventArgs
{
public int Index { get; set; }
}
searchForm sf = new searchForm(_QPCollection);
sf.SearchResultSelected += (s, e) => MyListView.SelectedIndex = e.Index;
If you set the Owner like you did, you should be able to call public methods inside the dialogue via (Owner as MyWindowDerivative).Method() (if Owner is of type Window), what exactly is stopping you from doing that?
Edit: If you are going to go that route you should make sure that Owner is always of type MyWindowDerivative, e.g. by overwriting the Owner-Property, also prevent parameterless constructors.
Does anyone have a piece of code to make all Controls (or even all TextBoxes) in a Form which is read-only at once without having to set every Control to read-only individually?
Write an extension method which gathers controls and child controls of specified type:
public static IEnumerable<T> GetChildControls<T>(this Control control) where T : Control
{
var children = control.Controls.OfType<T>();
return children.SelectMany(c => GetChildControls<T>(c)).Concat(children);
}
Gather TextBoxes on the form (use TextBoxBase to affect RichTextBox, etc - #Timwi's solution):
IEnumerable<TextBoxBase> textBoxes = this.GetChildControls<TextBoxBase>();
Iterate thru collection and set read-only:
private void AreTextBoxesReadOnly(IEnumerable<TextBoxBase> textBoxes, bool value)
{
foreach (TextBoxBase tb in textBoxes) tb.ReadOnly = value;
}
If want - use caching - #igor's solution
In Form:
if (_cached == null)
{
_cached = new List<TextBox>();
foreach(var control in Controls)
{
TextBox textEdit = control as TextBox;
if (textEdit != null)
{
textEdit.ReadOnly = false;
_cached.Add(textEdit);
}
}
}
else
{
foreach(var control in _cached)
{
control .ReadOnly = false;
}
}
Add recursion also (Controls can be placed into other controls (panels)).
You should be able to write yourself a utility function to do this. You can iterate over the form’s controls and then each control’s child controls recursively. For example:
public static void SetEnableOnAllControls(Control parentControl, bool enable)
{
parentControl.Enabled = enable;
foreach (Control control in parentControl.Controls)
SetEnableOnAllControls(control, enable);
}
[...]
// inside your form:
SetEnableOnAllControls(this, false);
This doesn’t take care of ToolStrips, which aren’t controls. You could write a separate, similar method for those.
Notice that the above disables the form itself too. If you don’t want that, try this:
public static void SetEnableOnAllChildControls(Control parentControl, bool enable)
{
foreach (Control control in parentControl.Controls)
{
control.Enabled = enable;
SetEnableOnAllChildControls(control, enable);
}
}
If you really meant the ReadOnly property, which is only relevant for TextBoxes, try this:
public static void SetReadOnlyOnAllControls(Control parentControl, bool readOnly)
{
if (parentControl is TextBoxBase)
((TextBoxBase) parentControl).ReadOnly = readOnly;
foreach (Control control in parentControl.Controls)
SetReadOnlyOnAllControls(control, readOnly);
}
I would use reflection to check to see if the generic Control object has an Enabled property.
private static void DisableControl(Control control)
{
PropertyInfo enProp = control.GetType().GetProperty("Enabled");
if (enProp != null)
{
enProp.SetValue(control, false, null);
}
foreach (Control ctrl in control.Controls)
{
DisableControl(ctrl);
}
}
this.Enabled = false;
Depends on what you doing really, you might want to consider putting the control within a panel and disabling that.
I haven't tested this, but it should work:
foreach (var textBox in this.Controls.OfType<TextBox>())
textBox.ReadOnly = true;
Edit: This is not such a good solution it seems: see Timwi's comment.
I just developed a recursive solution that handles any kind of Web Control, using a simple static method and ASP.NET polymorphysm.
/// <summary>
/// Handle "Enabled" property of a set of Controls (and all of the included child controls through recursivity)
/// By default it disable all, making all read-only, but it can also be uset to re-enable everything, using the "enable" parameter
/// </summary>
/// <param name="controls">Set of controls to be handled. It could be the entire Page.Controls set or all of the childs of a given control (property "Controls" of any Control-derived class)</param>
/// <param name="enable">Desired value of the property "enable" of each control. </param>
public static void DisableControls(ControlCollection controls, bool enable = false)
{
foreach (Control control in controls)
{
var wCtrl = control as WebControl;
if (wCtrl != null)
{
wCtrl.Enabled = enable;
}
if (control.Controls.Count > 0)
DisableControls(control.Controls, enable);
}
}
/// <summary>
/// Enable a set of Controls (and all of the included child controls through recursivity).
/// Friendly name for DisableControls(controls, true), that achieve the same result.
/// </summary>
/// <param name="Controls">Set of controls to be handled. It could be the entire Page.Controls set or all of the childs of a given control (property "Controls" of any Control-derived class)</param>
public static void EnableControls(ControlCollection controls)
{
DisableControls(controls, true);
}
This is tested and looks fairly fast (less than a millisecond in a web form with 25+ controls to be disabled).
If you prefer an extension method, i think it should be enough to change the solution as follows:
public static void DisableControls(this Control control, bool enable = false)
{
foreach (Control ctrl in control.Controls)
{
var wCtrl = ctrl as WebControl;
if (wCtrl != null)
{
wCtrl.Enabled = enable;
}
if (ctrl.Controls.Count > 0)
ctrl.DisableControls(enable);
}
}
public static void EnableControls(this Control control)
{
control.DisableControls(true);
}