How to get WPF UserControl's Parent Page in C#? - c#

I have a uCtrl of type UserControl in a parentPg of type Page (instead of a Window). And I want to access that parentPg in uCtrl class so that I can bind some uCtrl's property to parentPg's Control.
Like as we get in case of Window.
public partial class uCtrl : UserControl
{
Window parentWin;
public uCtrl()
{
parentWin = Window.GetWindow(this);
}
public bool IsPrptReady
{
get
{
//accesing parent Window's control
if (((pWin)(parentWin)).txt.Text != "")
{
return true;
}
else
{
return false;
}
}
}
}
In above code snippet, I am getting parent Window (i.e. of type pWin) in uCtrl of type UserControl and then sets its Property (i.e IsPrptReady) based on parents Window's Control i.e. txt of type 'TextBox'.
I want to do the same thing, but in case of Page not Window.
I have seen a lot of questions like this but nothing solves my problem.
Any help will be appreciated.
The XAML code is as below.
<Page x:Class="Stego.Pages.EnDeCoding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Test"
xmlns:userControls="clr-namespace:Test.UserControls"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="800"
>
<Grid>
<userControls:uCtrl x:Name="uCtrlName"/>
</Grid>
</Page>

The second answer in the question you linked to provides the answer you need; specifically the use of the VisualTreeHelper.
Alternatively, you could add a [dependency] property to the user control of type Page and bind the property to the page instance the control is hosted within. Something like:
<Page x:Name="page">
<local:UCtrl Page="{Binding ElementName=page}"/>
</Page>

Related

How can I change the instance of a UserControl from the containing Window's code behind?

In a more complex implementation of the below, I discovered a strange behavior.
I have a window, containing a UserControl. The UserControl requires a constructor with parameters, and is therefore reinitialized after the Window has called InitializeComponent().
Control XAML
<UserControl x:Class="TEMP5.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="33" d:DesignWidth="48">
<Grid>
<Button Content="Click"
Width="40" Height="25" Margin="4"
Click="Button_Click"/>
</Grid>
</UserControl>
Control C# code
public partial class MyControl : UserControl
{
public string Foo { get; set; }
public MyControl()
{
InitializeComponent();
}
public MyControl(string foo): this()
{
Foo = foo;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine($"This method was invoked on: {this.GetHashCode()} with Foo value: \"{Foo}\"");
}
}
Window XAML
<Window x:Class="TEMP5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TEMP5"
mc:Ignorable="d"
Title="MainWindow" Height="auto" Width="auto">
<local:MyControl x:Name="myControl"/>
</Window>
Window C# code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Console.WriteLine($"Parameter-less constructor creates control: {myControl.GetHashCode()}");
myControl = new MyControl("Bar");
Console.WriteLine($"Control is now referenced: {myControl.GetHashCode()} with Foo: {myControl.Foo}");
}
}
Console Output
Parameter-less constructor creates control: 66824994
Control is now referenced: 5560998 with Foo: Bar
Clicks the button
This method was invoked on: 66824994 with Foo value: ""
Question
Why is the UserControl instance not changed? The console output shows that the field is updated to reference the result of the "parametered" constructor, yet the button click is invoked on the original instance.
With credit and thanks to #Clemens for the answer provided in comments above. In summary:
Recommendation is to avoid property assignment through a constructor in place of an implementation that allows for reassignment. Suggestion to consider data templating as per: C# WPF content switching.
The observed behavior is due to the fact that the myControl field does not make reference to an instance assigned to the Content property of the Window.
Replacing myControl = new MyControl("Bar"); with Content = new MyControl("Bar"); would result in the expected behavior. However, again, this is not the recommended approach.
Thank you also to #MindSwipe for their contribution.

How to swap a portion of a uwp xaml interface with another on the same page and modify controls?

So I have a user interface which has several controls which I want to persist. One of these controls is a button that is supposed to swap between a view and an edit mode. Each of these will have many controls in them that will need to be accessible later on. My main page is defined as follows, with irrelevant stuff stripped down of course.
MapView.xaml
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TripPhotoMapper"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Maps="using:Windows.UI.Xaml.Controls.Maps"
x:Class="TripPhotoMapper.MapView"
mc:Ignorable="d">
<Grid x:Name="grid_main">
<Maps:MapControl x:Name="map_main" MapTapped="MapUserTapped"/>
<StackPanel x:Name="stack_edit_mode">
<Border Tapped="ButtonEditMode">
<TextBlock Text="Edit Mode"/>
</Border>
</StackPanel>
<Grid x:Name="grid_swap_interface">
here is where I want the swapable interfaces
</Grid>
</Grid>
</Page>
MapView.xaml.cs
namespace TripPhotoMapper
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MapView : Page
{
private EditMode _mode_edit;
private ViewMode _mode_view;
private Control _mode_current;
public MapView()
{
this.InitializeComponent();
_mode_edit = new EditMode();
_mode_view = new ViewMode();
_mode_current = _mode_view;
grid_swap_interface.Children.Add(_mode_current);
}
public static class GlobalVars
{
...
//this keeps track on if we are in edit mode
public static bool glo_edit_mode = false;
}
...
private void MapUserTapped(MapControl sender, MapInputEventArgs args)
{
...
//this is the problem
ViewMode.txtblc_view_mode.Text = "hi";
}
//editing controls
private void ButtonEditMode(object sender, TappedRoutedEventArgs e)
{
grid_swap_interface.Children.Clear();
if (GlobalVars.glo_edit_mode == false)
{
_mode_current = _mode_edit;
}
else
{
_mode_current = _mode_view;
}
grid_swap_interface.Children.Add(_mode_current);
}
}
}
I then have two other very barebones xaml files for the two interfaces.
EditMode.xaml
<UserControl
x:Class="TripPhotoMapper.EditMode"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TripPhotoMapper"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Button/>
</Grid>
</UserControl>
ViewMode.xaml
<UserControl
x:Class="TripPhotoMapper.ViewMode"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TripPhotoMapper"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<TextBlock x:Name="txtblc_view_mode" Text="view mode"/>
</Grid>
</UserControl>
I haven't touched the C# for either of these two additional xaml's yet. So they're just like this.
EditMode.xaml.cs
namespace TripPhotoMapper
{
public sealed partial class EditMode : UserControl
{
public EditMode()
{
this.InitializeComponent();
}
}
}
So I've figured out how to swap between the two interfaces without any issue; the button and textblock do both appear as they should. However, I included (for testing) a function to change the text in txtblc_view_mode to "hi", but I'm given the following error:
'ViewMode.txtblc_view_mode' is inaccessible due to its protection level
I'm incredibly new to splitting up files like this and I can't figure out how to fix it. I've found something mentioned in a few posts
x:FieldModifier="public"
but I don't know where to put it. Could anyone help me? Once the interfaces are properly split up, a lot of controls in each will have to be modified in code and thus I'll need to fix this problem.
Can't try this for now but you can check it out.
Add/modify the following code in your ViewMode cs:
public sealed partial class ViewMode : UserControl
{
public static ViewMode Current { get; private set; }
public ViewMode()
{
this.InitializeComponent();
Current = this;
}
}
And maintain the x:FieldModifier="public" in your desired control (TextBlock) to be edited/modified.
<TextBlock x:Name="txtblc_view_mode" x:FieldModifier="public" Text="view mode"/>
On EditMode cs, add in your methods:
public void ChangeTxtBlockText()
{
TextBlock tb = ViewMode.Current.txtblc_view_mode as TextBlock;
if (tb != null)
{
tb.Text = "Hi";
}
}

how to retrieve textbox string form mainpage in uwp?

I am new to UWP.
I have created an app with a MainPage and a UserControl.
I have a TextBox in that UserControl and I want to access its data from my Mainpage.
Can someone suggest a way to do this?
Thanks
While #Peter have a good answer but I think you also can use FieldModifier.
My UserControl is Averieli and my xaml is :
<UserControl
x:Class="Avalonia.Averieli"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Avalonia"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<TextBox x:Name="TextBox" x:FieldModifier="public"></TextBox>
</Grid>
</UserControl>
You can see that I add x:FieldModifier="public" in TextBox that can make it can be used in MainPage.
I use the UserControl in MainPage.
<local:Averieli x:Name="Averieli"></local:Averieli>
I also can use the TextBox in the back code.
public MainPage()
{
InitializeComponent();
Averieli.TextBox.Text = "I can use it";
}
In the constructor of the user control, initialise a static global instance of the UserControl class
public UserControl()
{
//Initialise control
}
public static UserControl UserControlInstance
{
get
{
return m_UserControlInstance ?? new UserControl();
}
}
private static UserControl m_UserControlInstance;
And then just access properties of UserControl via the instance
string x = UserControl.UserControlInstance.TextBox.Text;
This is called a singelton by the way, in case you wanted to look it up!

Get hard coded param from WPF UserControl to code behind

I 'm trying to do something really simple: I have a UserControl, where I want to pass a simple string parameter.
WPF MessagePage.xaml
<Page
x:Class="MuchroomPhone.MessagePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MuchroomPhone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
//...
<PivotItem Header="Nouveaux">
<local:MessageUC MessType="new"/>
</PivotItem>
<PivotItem Header="Lus" >
<local:MessageUC MessType="read"/>
</PivotItem>
<PivotItem Header="Envoyés" >
<local:MessageUC MessType="send"/>
</PivotItem>
<PivotItem Header="Tous" >
<local:MessageUC MessType="all"/>
</PivotItem>
//...
I want to get the MessType from the code behind of the MessageUC.
eg. : I want to get the string "new" in the MessageUC.xaml.cs
I've tried that so far:
MessageUC.xaml
<UserControl
x:Class="MuchroomPhone.MessageUC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MuchroomPhone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
//I don't think the content of the UC is important for my issue, but if you wanted to I can give it too.
MessageUC.xaml.cs
public sealed partial class MessageUC : UserControl
{
public string _messType;
public string MessType
{
get{ return _messType;}
set{this._messType = value;}
}
public ObservableCollection<Message> listMessages { get; set; }
public MessageUC()
{
this.InitializeComponent();
Debug.WriteLine(MessType);
this.fetchUserData();
}
But the MessType string is empty...
Any ideas how to achieve that?
PS: I think there should be a way less verbose way for do that, so if you know a "simple" trick, would be great!
Thanks
EDIT: So If I use a simple property, it's should work? Cause I still have null on MessType...
I've also tried with a Dependency Property, and MessType is an empty string.
EDIT 2: I think I understand what is wrong. Actually MessType doesn't exist on MessageUC.xaml. So the code behind can't find it. Perhaps isn it possible to just pass variable to my Page MessagePage.xaml directly to the User Control MessageUC?
all you need is move fetchUserData to loaded event:
public sealed partial class MessageUC : UserControl
{
public string MessType { get; set; }
public MessageUC()
{
InitializeComponent();
Debug.Writeline(MessType); //null
Loaded += MessageUC_Loaded;
}
public void MessageUC_Loaded(object sender, EventArgs e)
{
Debug.Writeline(MessType); //new
this.fetchUserData();
}
}
DependencyProperty is not needed! Your original code doesn't work, because ctor in invoked before the property is set. DependecyProperty does not solve this, but enables databinding, styling, animating of the property.
You only need a DependencyProperty if you want to data bind to it. In your case you can just use a normal property.
This is a set of instructions that should work. However you didn't show the definition of your control so the code below may need some modifications and adjustments.
So start with registering a dependency property:
public static readonly DependencyProperty _messTypeProperty =
DependencyProperty.Register("_messType", typeof(String),
typeof(MessageUC), new FrameworkPropertyMetadata(string.Empty));
public String _messType
{
get { return GetValue(_messTypeProperty).ToString(); }
set { SetValue(_messTypeProperty, value); }
}
Add a name to your control in XAML:
<UserrControl x:Class="myNamespace.MessageUC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
x:Name="MyUserControl">
In the XAML definition of your MessageUC control, in the code where you implement MessType bind it to the property in the code behind. Point to your control using the name you added to the control definition:
MessType="{Binding Path=_messType, ElementName=MyUserControl}"

How to remove child from UserControl?

It's maybe stupid question but I just cant figure out how to do this. I have a UserControl named "ReportUserControl" and inside of it I have a Grid named "ReportGrid" . I want to remove ReportGrid from ReportUserControl children. I tried this:
ReportUserControl control = new ReportUserControl();
control.Children.Remove(...);
Problem is that there is no .Children.Remove() option for UserControl. How can i manage this?
UPDATE
I want to "send" this grid to another class and use it there. Problem is that when I send the grid and try to use it in another class I get this: "Specified element is already the logical child of another element. Disconnect it first." so I have to remove it from my UserControl but don't know how.
ReportUserControl XAML:
<UserControl x:Class="WPFReportTest.ReportUserControl "
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Height="29.7cm" Width="21cm">
<Grid Name="ReportGrid">
****GridContent***
</Grid>
</UserControl>
ReportClass:
public class ReportingClass
{
Grid reportGrid;
public ReportingClass(Grid tempGrid)
{
reportGrid = tempGrid;
}
public Page SetPageContent()
{
Page page = new Page();
page.Content = reportGrid;
}
}
InstanceWindow
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ReportUserControl userControl = new ReportUserControl();
ReportingClass report = new ReportingClass(userControl.ReportGrid);
}
}
This is not the full code only small part to get idea of what I'm trying to manage. Line "page.Content = reportGrid;" is where the exception happens.
UserControl has Content property (msdn).
If you want to delete content set null:
ReportUserControl control = new ReportUserControl();
control.Content = null;
Example:
public class ReportingClass
{
ReportUserControl _reportUserControl;
Grid reportGrid;
public ReportingClass(ReportUserControl reportUserControl, Grid tempGrid)
{
_reportUserControl = reportUserControl;
reportGrid = tempGrid;
}
public Page SetPageContent()
{
Page page = new Page();
// _reportUserControl is the instance of object ReportUserControl where content is reportGrid
_reportUserControl.Content = null;
page.Content = reportGrid;
return page;
}
}
Edit:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ReportUserControl userControl = new ReportUserControl();
ReportingClass report = new ReportingClass(userControl, userControl.Content as Grid);
}
}
#Stojdza, in WPF, we generally don't manipulate UI elements in that way. We manipulate data elements and declare DataTemplates to define what that data looks like. So in your case, you shouldn't literally move the Grid... just move the data and let WPF regenerate your Grid wherever you want using the same DataTemplate. It will look just the same, but it's a whole lot easier and you'll save yourself all of this trouble.
So, if you're interested in doing this the easy way, first create a class that has all of the properties required in the UserControl, let's call it Data. Now declare a DataTemplate in Application.Resources so that you can use it application-wide:
<DataTemplate DataType="{x:Type YourNamespacePrefix:Data}">
<!-- Define your Grid here -->
<DataTemplate>
Now whenever you want to display your Grid, whether in a collection, or individually, you just need to bind a property of type Data (or a collection of them) to some form of ContentControl:
public Data Data { get; set; }
...
<ContentControl Content="{Binding Data}" />
So rather than trying to copy the Grid, you just copy the data to another property of type Data and recreate the Grid with the single line above. See the Data Templating Overview page on MSDN for more information on DataTemplates.

Categories