WPF call method parent from usercontrol - c#

I want to call a method from user control in WPF
I´ve a Window with a list and I have a method that get this list.
private ObservableCollection<int> _lst;
public MainWindow()
{
InitializeComponent();
getList();
}
public void getList()
{
_lst = List<int>();
}
In this page I use a usercontrol:
UserControlAAA userControl = new UserControlAAA ();
gridDatos.Children.Add(userControl);
I want to do something like this inside of usercontrol:
Window win = Window.GetWindow(this);
win.getList();
but I can´t call win.getList();
I want to call the method getList from my usercontrol, but I don´t know how to do it.

You'll need to cast the Window object to the specific window type you're using - which in your case is MainWindow:
MainWindow win = (MainWindow)Window.GetWindow(this);
win.getList();
However, it's not wise to have such coupling between the user control and the window it's hosted in, since that means you will only be able to use it in a window of type MainWindow. It would be better to expose a dependency property in the user control and bind the list to that property - this way the user control will have the data it requires and it will also be reusable in any type of window.

Solution from #Adi Lester is working but it break WPF coding ruler. Proper way of doing this is using event like in the linked answer below
https://stackoverflow.com/a/19384953/3099317

Related

How do I use a relaycommand on startup and how do I do it if the method I want to pass is within another class

I am interested in learning more about MVVM. I have taken a look at the MVVM Demo App. I understand many of the major concepts behind it.
When I began playing with the app, I wanted to open one of the tabviews by default upon the app starting up. However I am unsure on how to do that.
In the app, I think I understand that when a control panel button is clicked (e.g. View All Customers), the commandrelay creates a new AllCustomersViewModel and the data template applies the view to the viewmodel, the new workspace is created to the Workspaces collection and the tab opens because of the databinding in the main window.
I have no idea how to start this process other than clicking the hyperlink. I know that I need to call new RelayCommand(param => this.ShowAllCustomers()) but I don't understand how to call this without any user interaction, or how to call it from outside of the mainwindowviewmodel, e.g. from the app's onstartup method.
Can someone please advise on the best way to use a relaycommand on the start up of an app? Also, how do I use a relaycommand if the method I want to pass is within another class?
VMaleev has correctly given me a method to call the command, however the example provided was specific to a the collection of commands. What if I have a method Public ICommand HelpPageCommand which creates a new command based on a ShowHelpPage method where ShowHelpPage is;
HelpViewModel workspace = new HelpViewModel();
this.Workspaces.Add(workspace);
this.SetActiveWorkspace(workspace);
How would I call this command then?
- Simple, if the method is ICommand, then simply method.execute(null)
I suppose, you are talking about this article.
To call RelayCommand without user interaction, you just should write:
If want to call from MainWindowViewModel (for example, in constructor):
_commands.FirstOrDefault(q => q.DisplayName == Strings.MainWindowViewModel_Command_ViewAllCustomers).Command.Execute(null);
If want to call from App.xaml.cs (on application startup, code is taken from example and only one line added) or something else place where you have access to view model instance:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
// Create the ViewModel to which
// the main window binds.
string path = "Data/customers.xml";
var viewModel = new MainWindowViewModel(path);
// When the ViewModel asks to be closed,
// close the window.
EventHandler handler = null;
handler = delegate
{
viewModel.RequestClose -= handler;
window.Close();
};
viewModel.RequestClose += handler;
// Allow all controls in the window to
// bind to the ViewModel by setting the
// DataContext, which propagates down
// the element tree.
window.DataContext = viewModel;
// the following line is added
viewModel.Commands.FirstOrDefault(q => q.DisplayName == Strings.MainWindowViewModel_Command_ViewAllCustomers).Command.Execute(null);
window.Show();
}
If the method you want to pass is in another class, there are two ways to do it:
pub/sub mechanism (by using c# events)
have access from every instance of view model to all instances of your view models. In this case you are able to pass method of any instance of view model as parameter of RelayCommand
Hope, it helps

Create property of UserControl Instances (not new)

I have a usercontrol where I want a property that can list all the other instances of the same usercontrols in the Windows Form.
Eg. I have a simple usercontrol (sidebarbutton). I drag-drop 2 instances of it in a UserForm. Now I want a property (in the usercontrol itself) that can list both of them.
I have written this property. However, when used in Property Browser Window of Visual Studio, it allows me to add new instances of sidebarButton control.
private List<SidebarButton> _sidebarButtons;
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always), Category("Roshan")]
public List<SidebarButton> SidebarButtons
{
get { return this._sidebarButtons; }
set { this._sidebarButtons = value; }
}
How to add the 2 instances that I drag-droped in the form in this property.
I know the property needs to be modified but don't have a proper direction to take. Please HELP me.
One way of doing this could be that upon addition of the user control to the form, the user control notifies all other user controls of that type that it now exists. Your user control can implement an interface like so:
public interface INotifiable
{
public void AddToList(INotifiable newButton);
}
When the user control is added to the form you iterate through all the controls and check whether they implement this interface. If they do you call the AddToList method and pass the newly added control to it.

How can I access a control in WPF from another class or window

I want to access my controls like button or textbox in mainWindow in WPF, but I can't do this.
In Windows Form application it's so easy, you can set modifier of that control to True and you can reach that control from an instance of that mainWindow, but in WPF I can't declare a public control. How can I do this?
To access controls in another WPF forms, you have to declare that control as public. The default declaration for controls in WPF is public, but you can specify it with this code:
<TextBox x:Name="textBox1" x:FieldModifier="public" />
And after that you can search in all active windows in the application to find windows that have control like this:
foreach (Window window in Application.Current.Windows)
{
if (window.GetType() == typeof(Window1))
{
(window as Window1).textBox1.Text = "I changed it from another window";
}
}
Unfortunately, the basics of WPF are data bindings. Doing it any other way is 'going against the grain', is bad practice, and is generally orders of magnitude more complex to code and to understand.
To your issue at hand, if you have data to share between views (and even if it's only one view), create a view model class which contains properties to represent the data, and bind to the properties from your view(s).
In your code, only manage your view model class, and don't touch the actual view with its visual controls and visual composition.
I found that in WPF, you have to cast Window as a MainWindow.
Looks complicated but it's very easy! However, maybe not best practices.
Supposing we have a Label1, a Button1 in the MainWindow, and you have a class that deals with anything related to the User Interface called UI.
We can have the following:
MainWindow Class:
namespace WpfApplication1
{
public partial class MainWindow : Window
{
UI ui = null;
//Here, "null" prevents an automatic instantiation of the class,
//which may raise a Stack Overflow Exception or not.
//If you're creating controls such as TextBoxes, Labels, Buttons...
public MainWindow()
{
InitializeComponent(); //This starts all controls created with the XAML Designer.
ui = new UI(); //Now we can safely create an instantiation of our UI class.
ui.Start();
}
}
}
UI Class:
namespace WpfApplication1
{
public class UI
{
MainWindow Form = Application.Current.Windows[0] as MainWindow;
//Bear in mind the array! Ensure it's the correct Window you're trying to catch.
public void Start()
{
Form.Label1.Content = "Yay! You made it!";
Form.Top = 0;
Form.Button1.Width = 50;
//Et voilá! You have now access to the MainWindow and all it's controls
//from a separate class/file!
CreateLabel(text, count); //Creating a control to be added to "Form".
}
private void CreateLabel(string Text, int Count)
{
Label aLabel = new Label();
aLabel.Name = Text.Replace(" ", "") + "Label";
aLabel.Content = Text + ": ";
aLabel.HorizontalAlignment = HorizontalAlignment.Right;
aLabel.VerticalAlignment = VerticalAlignment.Center;
aLabel.Margin = new Thickness(0);
aLabel.FontFamily = Form.DIN;
aLabel.FontSize = 29.333;
Grid.SetRow(aLabel, Count);
Grid.SetColumn(aLabel, 0);
Form.MainGrid.Children.Add(aLabel); //Haha! We're adding it to a Grid in "Form"!
}
}
}
var targetWindow = Application.Current.Windows.Cast<Window>().FirstOrDefault(window => window is principal) as principal;
targetWindow .BssAcesso.Background = Brushes.Transparent;
just call any control of it from your current window:
targetWindow.ABUTTON.Background = Brushes.Transparent;
How can I access one window's control (richtextbox) from another window in wpf?
I was also struggling with this when I started WPF. However, I found a nice way around it similar to the good old fashioned win forms approach (coding VB.NET, sorry). Adding on what was said earlier:
To directly change properties of objects from a module or a different class for an active window:
Public Class Whatever
Public Sub ChangeObjProperties()
' Here the window is indexed in case of multiple instances of the same
' window could possibly be open at any given time.. otherwise just use 0
Dim w As MainWindow = Application.Current.Windows(0)
w.Button1.Content = "Anything"
End Sub
End Class
You obviously have to instantiate Whatever before ChangeObjProperties() can be called in your code.
Also there is no need to worry about naming in XAML regarding object accessibility.
Just declare your control like this to make it public:
<TextBox x:Name="textBox1" x:FieldModifier="public" />
You can then access it from another control.
The default declaration of controls is non public, internal and not public!
Access of the controls from within the same assembly is hence allowed. If you want to access a control on a wpf form from another assembly you have to use the modifier attribute x:FieldModifier="public" or use the method proposed by Jean.
This may be a slightly different answer, but let's think about why we need to pass data between forms.
obviously, the reason is 'visualization'.
use Delegate or Event.
There is no need to declare an element as Public just to make it visible.
only need to be able to transform elements within a window using a delegate , on a limited basis.
To access any control in another window is so simple. Lets say to access from a Login window to MainWindow. Here is the steps:
MainWindow MW = new MainWindow(); //declare the mainwindow
MW.Label1.Content = "Hello world"; //specify what control
MW.ShowDialog(); //check what happen to that control
Good programming

Why do I get an error in the MainWindow when trying to initialize a textbox in a UserControl

I created a UserControl that contains a textbox. When I try to initialize the textbox in the constructor of the UserControl, with some text like this
public FileSelector()
{
InitializeComponent();
TB_FolderPath.Text = #"c:\tmp\Test\";
}
I get an error in the MainWindow.xaml
Cannot create an instance of "FileSelector".
When I remove the row
TB_FolderPath.Text = #"c:\tmp\Test\";
I don't get the error, but of course an empty textbox.
Previously when I had the parts of the UserControl integrated in the MainWindow, there was also no problem.
I tried to create a simpler version of MainWindow using a UserControl to reproduce the problem, but in a simple case it works.
So, my questions.
What can be the cause of the problem?
How can I debug/analyze a problem like this systematically? I just get the this error in VisualStudio after building, without an explanation.
How/Where can I initialize the controls in a UserControl. In general, is the UserControl the right place to initialize the controls or would the MainWindow be also a possibility? (Is this possible at all?)
In WPF, unlike WinForms, the controls are not initialized completely after InitializeComponent(). Hence the uninitialized/unloaded controls throw errors.
You need to write handler to capture the Loaded event of the control.
Read Object Lifetime Events.
Get some more detailed info here.
Example (partially taken from OP's code):
public FileSelector()
{
InitializeComponent();
TB_FolderPath.Loaded += delegate { TB_FolderPath.Text = #"c:\tmp\Test\"; }
}

WPF: adding a usercontrol to a window

First off, I'm new to WPF and C# so maybe the issue I have is really easy to fix. But I'm kinda stuck at the moment.
Let me explain my problem.
I have a WPF Window and two usercontrols (Controls and ContentDisplayer).
The usercontrol Controls, wich contains some buttons, is added in the XAML of the Window.
Nothing special here.
Window.XAML
<nv:Controls/>
Now, what I want to do is when a user is pressing a button in Controls, ContentDisplayer needs to be added to the Scatterview I have in my Window.
I solved the problem by adding the buttons to the Window, and not using the usercontrol Controls. But this is not what I want.
Window.XAML.CS
private static void Button_ContactChanged(object sender, ContactEventArgs e)
{
object ob = Application.LoadComponent(new Uri(
"NVApril;component\\XAML\\ContentDisplayer.xaml",
System.UriKind.RelativeOrAbsolute));
//Set a unique name to the UserControl
string name = String.Format("userControl{0}",
SurfaceWindow1_Scatterview.Items.Count);
UserControl userControl = ob as UserControl;
userControl.Name = name;
//Add the new control to the Scatterview
SurfaceWindow1_Scatterview.Items.Add(userControl);
SurfaceWindow1_Scatterview.RegisterName(name, userControl);
}
So the real question is: How do I add a usercontrol to the Window by pressing a button in an other usercontrol?
Thanks,
Toner
At the top of the window xaml add
xmlns:d="clr-namespace:SomeNamespace.Usercontrols"
where you these exist already, you can choose the namespace of your control from the intellesence list.
Then where you want to place the control type:
<d:yourusercontrolhere params />
and your usercontrols can be added there.
Within Controls expose an event that is fired when you want to add a new control.
public event EventHandler AddControl;
private void RaiseAddControl()
{
if (AddControl!= null)
{
AddControl(this, EventArgs.Empty);
}
}
Now sink that event in your Window
yourControl.AddControl += ContactChanged
In your window, it sounds like you need to add the event to the instances of Controls.
<local:ContentDisplayer>
...
<nv:Controls AddControl="ContactChanged"/>
...
Then in your ContactChanged event handler you can instantiate a new Controls control and add it to whatever collection you're using like in your Button_ContactChanged event handler above.
Let me know if you need further clarification.
I have no idea what you are trying to do your example,
So you have a control defined thus:
public partial class somecontrolname : UserControl
With your corresponding Xaml file
All you need to do to add it in code to your window is firstly you need a LayoutRoot such as Grid control in the window then just
[mylayoutcontrol].Children.Add(new somecontrolname());
Maybe I got wrong idea what you are trying to do, your example code doesn't make much sense to me, looks like you are trying to load the xaml source file

Categories