WPF how to access MainPage elements from Popup Page - c#

I am building a WPF app that needs numeric input from user. I am using NavigationWindow and have built a Page that contains a numeric keypad. The app needs numeric input more than once. Inspired by this, I want to put numeric Page inside a Frame inside a Popup as follows:
<Grid Name="mainDisplay">
<Popup Name="popupDialog" PlacementTarget="{Binding ElementName=aInputBox Placement="Center">
<StackPanel>
<Frame Source="MyNumpad.xaml"/>
<Button Click="OK_Outside_Click">Ok_Outside_Numpad</Button>
</StackPanel>
</Popup>
// invalid comment and other stuff in grid....
</Grid>
When the "Ok_Outside_Numpad" Button is clicked, the numpad disappears and the main page is re-enabled. Code as follows:
private void OK_Outside_Click(object sender, RoutedEventArgs e)
{
popupDialog.IsOpen = false;
mainDisplay.IsEnabled = true;
}
Two questions:
There is already an "OK" button inside MyNumpad.xaml. Is it possible to access the mainDisplay and popupDialog clicking Ok_Inside_Numpad button? How can I pass variables to
MyNumpad constructor?
How to get the user numeric input when MyNumpad exits?
Thanks! Note that I am new to WPF, not sure if my approach is any good. Any idea is welcomed!

The correct approach is binding the Views (NavigationWindow and MyNumpad.xaml) to a ViewModel (or several ViewModels). With data binding, you simply set a value to false to close the Popup (since the Popup's IsOpen property is bound to that value). It does not matter where a control resides since you don't need to manipulate controls in your code.
But before reading that long article and begin to learn MVVM, you can make some modification to your code to make it work.
You can't access MainPage elements from the hosted page, as the hosted page has no reference to the MainPage. As you have thought about, you can pass MainPage to the constructor of MyNumpad (do it in code, and remember to modify MyNumpad's constructor).
public MainWindow()
{
InitializeComponent();
MyNumpad numpad = new MyNumpad(this);
frame1.Navigate(numpad);
}
Then you can access MainPage elements from the Popup Page.
But it is easier to add a Click event handler to the Popup's button, because the event handler is defined inside MainWindow, it has access to all controls in MainWindow. (Remember to delete the event handler defined inside MyNumpad.)
string strNumber;
public MainWindow()
{
InitializeComponent();
MyNumpad numpad = new MyNumpad();
numpad.button1.AddHandler(Button.ClickEvent, new RoutedEventHandler(
(s, e) =>
{
//suppose there is a TextBox in the page to accept user input
strNumber = numpad.InputTextBox.Text;
popupDialog.IsOpen = false;
mainDisplay.IsEnabled = true;
}), false);
frame1.Navigate(numpad);
}

Related

Using data from a ViewModel in a ModalDialog even though it lives in the Shell

I'm writing a UWP and using Template 10.
I've created a ModalDialog that should show the user some data that was just calculated in a ViewModel.
Here's where I'm lost:
#1, the ModalDialog needs data from my ViewModel. #2, the ModalDialog needs to call 1+ method(s) on the ViewModel depending on which button the user clicks.
My Shell.xaml.cs:
public sealed partial class Shell : Page
{
public static Shell Instance { get; set; }
public static HamburgerMenu HamburgerMenu => Instance.MyHamburgerMenu;
public Shell()
{
Instance = this;
InitializeComponent();
if (App.MobileService.CurrentUser == null)
LoginModal.IsModal = true;
}
public Shell(INavigationService navigationService) : this()
{
SetNavigationService(navigationService);
}
public void SetNavigationService(INavigationService navigationService)
{
MyHamburgerMenu.NavigationService = navigationService;
}
#region Login
private void LoginLoggedIn(object sender, EventArgs e)
{
MyHamburgerMenu.NavigationService.Navigate(typeof(Views.MainPage));
LoginModal.IsModal = false;
}
#endregion
}
}
Shell.xaml
<Controls:ModalDialog x:Name="ScoreModal" Grid.RowSpan="3"
CanBackButtonDismiss="False"
DisableBackButtonWhenModal="True">
<Controls:ModalDialog.ModalContent>
<myControls:QuizScorePart
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Controls:ModalDialog.ModalContent>
</Controls:ModalDialog>
What I've tried:
I tried just putting the control for the ModalDialog in the View that uses the ViewwModel I wish to speak to, but that doesn't work; that view lives inside the shell, meaning everything underneath the ModalDialog is NOT disabled. It HAS to be in the Shell, from what I know.
I tried setting up a method in the Shell.xaml.cs file that sets IsModal for my dialog to true/false; this works, but it doesn't solve my problem of interacting with my ViewModel.
I'm lost. Thanks anyone for the help.
reference the Search example, there are delegates to handle in the actual part, look at the codebehind for the LoginPart....
What I specified below in comments is how I do it with a LoginPage, not a usercontrol. Which has a LoginPageViewModel which in-turn has a the SettingsService instance referenced.
Edit
Think of it this way...ScoreModal isn't anything other than another view control. QuizScorePart is your View, I assume that QuizScorePartViewModal exists. From there it becomes a message passing exercise between viewmodels. At least that is what I see after the last comment. You need to know what the user clicked on button wise. Assuming that the above is true, then QuizViewModel would react to that message it was listening for. Shell is merely a holding location for a complete screen cover for the it only reacts to IsModal. If that is an issue consider a Service to hold the button selection, similar to how SettingsService works. Nothing says that QuizScorePart couldn't have its datacontext set to that of the QuizViewModel, but it might be a testing issue at that point.

Showing Progressring during navigation between pages

My intention is that when a user clicks a button to navigate to another page he will see a progressring untill the other page is fully loaded.
I have found a lot of tutorials about the subject, but all of them use code behind. I want to be able to use no code-behind(if possible). I am currently using MvvmLight with the ViewModelLocater.
I have allready made a dummy grid in my views that can display the Progressring and i have bound its active and visible property's to my viewmodels.
<Grid Background="Transparent" Visibility="{Binding LoadCircleVisibility}" Height="{Binding HeigthScreen}" Width="{Binding WidthScreen}" >
<ProgressRing Height="50" Width="50" IsActive="{Binding ProgressRingActive}" Visibility="{Binding ProgressRingVisibility}"></ProgressRing>
</Grid>
I can allready display it when i'm getting data from a wcf service for example, but not when the user navigates to a different page.
At this point im trying to do this with messaging, but i am not sure where i should put the message that will make my progressring visible.
My message will always go through this method to set the correct values for the progressring:
private void ProggressBarVisible(bool visible)
{
if (visible)
{
ProgressRingVisibility = Visibility.Visible;
LoadCircleVisibility = Visibility.Visible;
}
else
{
ProgressRingVisibility = Visibility.Collapsed;
LoadCircleVisibility = Visibility.Collapsed;
}
ProgressRingActive = visible;
}
But when i want to navigate to another page there is no progressring.
So the Question is how can i display a progressring while mvvmlight is loading the new page.
Edit: partial solution:
The reason my navigation was slower and locked the uithread was because it had to buid the entire page. I changed this by making sure all the methods and logic is ut into a different thread, this ensures that my uithread continues immediatly and i can display the loadcircle on my second page while it is performing the logic and methods.
I don't know how do this without code behind
You can add root panel to the your application and put there custom control. This custom control just for showing/hiding preloader.
And when you unload 1st page(or another event) you'll show custom preloader; and when you finish to download 2nd page- you'll hide custom preloader.

Windows form C# change user control by code

I have a windows form and i dont want to make any other windows forms just one windows form and different user controls how can i change between user controls for example hide one and show the other user control programmatically ?
private void Btt_info_Click(object sender, EventArgs e)
{
Frm_Main frm_main = new Frm_Main();
frm_main.Controls["panel1"].Controls.Clear();
UC_Info uc_info = new UC_Info();
frm_main.Controls["panel1"].Controls.Add(uc_info);
}
i added this but it doesnt work
Add a container control (if I remember correctly, there's a containers section in the toolbox?), like a panel. Create usercontrols for what you want to dynamically switch around. So make like a 'HomePage' usercontrol and a 'LoginPage' usercontrol. Dynamically add the usercontrol that you want to display to the container. WHen you want, remove it from the container and add a different usercontrol:
Panel myPanel = new Panel();
LoginPage ctlLoginPage = new LoginPage();
HomePage ctlHomePage = new HomePage();
//add the loginpage to the panel first
myPanel.Controls.Add(ctlLoginPage);
...do stuff...
//remove whatever control is currently in the Panel
myPanel.Controls.Clear();
//add the other control, the HomePage control instead now
myPanel.Controls.Add(ctlHomePage);
..do other stuff...
I usually do it this way so you leave your form itself open to add common controls and stuff that might be shared between your different 'pages'.
EDIT: Note that I normally would add the panel in the designer and not create it dynamically in the code. This was just an example.
EDIT: The interaction between your mainform and usercontrols can be handled in a few different ways, and I am not saying that any of these is the correct method.
You create a static property for your Panel on the Mainform, so that
you can always access it to swap your controls around.
In this example I'll also add a static method for it
enum PanelControlsEnum {HomePage, LoginPage};
public static Panel MyContainerPanel {get;set;}
public static void SwitchPanelControls(PanelControlsEnum selControl){
..put your switch panels code here..
}
Then inside your usercontrol you call a predefined method, something like:
MainForm.SwitchPanelControls(PanelControlsEnum.HomePage);
Another method is to bind the button click event on your mainform
instead of inside the form.
Like This:
HomePage ctlHomePage = new HomePage();
ctlHomePage.Click += MyClickEvent;
myPanel.Controls.Add(ctlHomePage)
...
private void MyClickEvent(object sender, RoutedEventArgs e)
{
..switch user control code here...
}
Create a method that returns a UserControl object. Then put conditions in that method as to which control you want to load at a specific condition and then in your main form code.
UserControl control = GetControlFromMyMethod();
form1.Controls.Add(control);
where 'control' is the returned control from your method.
To remove the existing one you have to loop through the form1.Controls and find out the control and call 'Remove'.
Update:
Mike C has a better idea of adding a panel and loading your desired control on the panel as then it's easy to remove your control and you then don't have to loop through the forms controls to find it and then remove it.
Try this:
this.Controls.Clear();
usercontrol load = new usercontrol ();
this.Controls.Add(load);
load.Show();
you could try this it will definitely help you as it did helped me a lot it short and straight to the point hope that will help

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

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