How to send TextBox value from modal window to main window programmatically? - c#

I'm trying to learn WPF MVVM I would need to understand how to update a textbox value via a modal window. Below the code, I wrote passes the value to the viewmodel but does not update the textbox. Thanks in advance
UserControl con il TextBox
<TextBox x:Name="Text01UC" Text="{Binding TextUC, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Width="200" Height="33"/>
UserControl Behind
namespace InvioDati
{
public partial class textbox : UserControl
{
public textbox()
{
InitializeComponent();
var vm = new ModelTextView();
this.DataContext = vm;
vm.Load();
}
private void Open_Click(object sender, RoutedEventArgs e)
{
MoadalWindow md = new MoadalWindow();
md.ShowDialog();
}
}
}
ModelTextView
namespace InvioDati
{
class ModelTextView : BaseViewModel
{
private ModelText dati = new ModelText();
public string TextUC
{
get => dati.TextVal;
set
{
dati.TextVal = value;
OnPropertyChanged();
}
}
public void Load() {
TextUC = "GoodMorning";
}
public void Ricevi(string valore)
{
TextUC = valore;
}
}
}
ModalWindow Code behind
namespace InvioDati
{
public partial class MoadalWindow : Window
{
public MoadalWindow()
{
InitializeComponent();
}
private void Test_Click(object sender, RoutedEventArgs e)
{
ModelTextView nd = new ModelTextView();
nd.Ricevi(Send.Text);
this.Close();
}
}
}

Set the DataContext of the ModalWindow to the same instance of ModelTextView in textbox.xaml.cs:
private void Open_Click(object sender, RoutedEventArgs e)
{
MoadalWindow md = new MoadalWindow();
md.DataContext = this.DataContext;
md.ShowDialog();
}
You can then either bind directly to the TextUC property or do the following in ModalWindow.xaml.cs:
private void Test_Click(object sender, RoutedEventArgs e)
{
ModelTextView nd = DataContext as ModelTextView;
nd.Ricevi(Send.Text);
this.Close();
}

You must use a mediator in order not to break mvvm here.
Check https://en.wikipedia.org/wiki/Mediator_pattern#C#
1b. Add Observer pattern to create notifications for value changes.
Dialogs are evil within MVVM, usually you won't need them. What you want is an overlaying View, which can be Data bound in any way as there is no break in the visual tree
If you want to use "dialogs", implement a DialogService to do so.
Edit: here is a draft on how you create something "popup" like in the most simple way:
<UserControl>
<Grid>
<!--Invert visability of all controls below via binding-->
<YourMainControl/>
<Rect Fill="Black" Opacity=".5 Visibility="Hidden"/>
<YourSubControl Visibility="Hidden"/>
</Grid>
</UserControl>

Related

Change view using a button click in WinUI 3

In WinUI 3 I want to change the view to a SecondaryView after a button click. The view change works flawlessly if I just add it to my code. But as soon as it happens in a Button Click function the app crashes. I am using the Template Studio for WinUI template to do this. The relative code is as follows:
MainPage.xaml:
<Grid x:Name="ContentArea">
<TextBlock Text="Main Page"/>
<Button Content="Press" Click="Button_Clicked"/>
</Grid>
MainPage.xaml.cs
private readonly INavigationService _navigationService;
public MainPage()
{
ViewModel = App.GetService<MainViewModel>();
InitializeComponent();
_navigationService.NavigateTo(typeof(SecondaryViewModel).FullName); // WORKS
}
private void Button_Clicked(object sender, RoutedEventArgs e)
{
_navigationService.NavigateTo(typeof(SecondaryViewModel).FullName); // DOESN'T WORK
}
The exception I get is
#if DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
UnhandledException += (sender, e) =>
{
if (global::System.Diagnostics.Debugger.IsAttached) global::System.Diagnostics.Debugger.Break();
};
#endif
This is all right from the template, barely changing anything. I tried it in my own code first before trying the template and got the same error. Is there any way to change the view on a button click?
You need to initialize the _navigationService.
public sealed partial class MainPage : Page
{
private readonly INavigationService _navigationService;
public MainViewModel ViewModel
{
get;
}
// Constructor
public MainPage()
{
ViewModel = App.GetService<MainViewModel>();
InitializeComponent();
_navigationService = App.GetService<INavigationService>(); // This line is missing.
_navigationService.NavigateTo(typeof(SecondaryViewModel).FullName);
}
private void Button_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
_navigationService.NavigateTo(typeof(SecondaryViewModel).FullName);
}
}

How do I access the content of the frame in different class? WPF

I wanted to change the frame's content to the Register.xaml page when the RegisterButton is click but it doesn't do it. How do I properly access the frame from the MainWindow?
Please help! I've been here long enough.
MainWindow.xaml
<Frame x:Name="MainFrame" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3"
Padding="10" BorderBrush="DarkGray" BorderThickness="2"/>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainFrame.Content = new Navigation();
}
public void GoToRegister()
{
MainFrame.Content = new Register();
}
}
Navigation.xaml
<Button x:Name="RegisterButton" Grid.Row="0" Grid.Column="0" Background="LightSteelBlue" Content="Register" FontSize="24" Margin="10" Click="RegisterButton_Click"/>
Navigation.xaml.cs
public partial class Navigation : Page
{
public Navigation()
{
InitializeComponent();
}
private void RegisterButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("success");
MainWindow Main = new MainWindow();
Main.MainFrame.Content = new Register();
}
}
In your Navigation you're creating a new MainWindow (which you don't display) and this is why it's not working. If you want to access MainWindow from underlying classes (I wouldn't do it, but it's a different story), you can, for example, create a public static method that takes a Page as an input parameter and sets it as the content for MainFrame:
In your MainWindow.xaml.cs
public static void SetMainFrameContent(Page page)
{
this.MainFrame.Content = page;
}
And then in your Navigation.xaml.cs button click event handler you call this method:
private void RegisterButton_Click(object sender, RoutedEventArgs e)
{
MainWindow.SetMainFrameContent(new Register());
MessageBox.Show("success");
}
What also may help is setting the data context of each of the windows, simply call this.DataContext = this; in the constructor.

WPF Command Binding on Code Behind problem

I am new to WPF and I have created a WPF Application. In that application, I have a UserControl that contain a button as below,
<UserControl.DataContext>
<local:AppViewModel/>
</UserControl.DataContext>
<Grid>
<Button x:Name="Btn_Contact" Command="{Binding BookVM.LoadContactsCommand}" Click="Btn_Contact_Click"/>
</Grid>
And My AppViewModel Class is as below
public AppViewModel()
{
var dataService = new JsonContactDataService();
BookVM = new BookViewModel(dataService);
CurrentView = BookVM;
}
My problem is I want this UserControl to run command of the Btn_Contact automatically when the UserControl is loaded instead of clicking the button. I have try to write the command binding on the UserControl code-behind but it does not worked.
public UserControlMemo()
{
InitializeComponent();
Btn_Contact.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
Btn_Contact.SetBinding(Button.CommandProperty, new Binding("BookVM.LoadContactsCommand"));
}
In its simplest form:
public UserControlMemo()
{
Loaded += OnLoaded;
InitializeComponent();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (DataContext is AppViewModel viewModel)
viewModel.BookVM.LoadContactsCommand.Execute(null);
}

Changing Grid Background Image of MainWindow from another Window's button - WPF

Iam trying to change the image of the MainWindow's Grid, which is a room with no lights, by pressing a button on another Window, and come up with a room with lights background. The problem is that when i edit the onClick event of the button i cant change the MainWindow's Grid Background.
XAML of MainWindow's Grid:
<Grid x:Name="PIC">
<Grid.Background>
<ImageBrush ImageSource="lightsofflaptop.jpg"/>
</Grid.Background>
And the Window's Code:
public partial class Window3 : Window
{
public Window3()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
ImageBrush b1 = new ImageBrush();
b1.ImageSource = new BitmapImage(new Uri(#"---\lightsonNOlaptop.jpg"));
}
}
i cant connect b1 with the Grid Background of the MainWindow.
Thanks in advance.
**Edit
I navigate through Windows by this:
*button located in MainWindow.cs
private void button_Click(object sender, RoutedEventArgs e)
{
var newForm = new Window1();
newForm.Show();
this.Close();
}
It appears that you are a novice to WPF.
I would make a ViewModel and use this ViewModel in both Windows, and set the background property in Window3 and it will get reflected in MainWindow by Binding.
ViewModel
using System.ComponentModel;
using System.Windows.Media;
namespace WpfDatabase
{
public class ViewModel:INotifyPropertyChanged
{
private ImageBrush _background;
public ImageBrush Background
{
get { return _background; }
set { _background = value; OnPropertyChanged("Background"); }
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void OnPropertyChanged(string prop)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
}
MainWindow.xaml
<Grid Background="{Binding Background}">
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public ViewModel VM
{
get { return this.DataContext as ViewModel; }
set { this.DataContext = value; }
}
public MainWindow()
{
this.VM = new ViewModel();
InitializeComponent();
this.VM.Background = new ImageBrush(new BitmapImage(new Uri(#"C:\Users\Public\Pictures\Sample Pictures\Koala.jpg")));
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Window3 win = new Window3(VM);
win.Owner = this;
win.Show();
}
}
Window3
public partial class Window3 : Window
{
public ViewModel VM
{
get { return this.DataContext as ViewModel; }
set { this.DataContext = value; }
}
public Window1(ViewModel vm)
{
InitializeComponent();
VM = vm;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
this.VM.Background = new ImageBrush(new BitmapImage(new Uri(#"C:\Users\Public\Pictures\Sample Pictures\Desert.jpg")));
}
}
See this
If you are opening Window3 from the Window with the Grid that needs the image changed, you could pass a reference of the grid window in the constructor
Like this:
(In Window 3 Code)
private Window1 _parentWindow = null;
public Window3(Window1 parent)
{
_parentWindow = parent;
InitializeComponents();
}
private void button_Click(object sender, RoutedEventArgs e)
{
ImageBrush b1 = new ImageBrush();
b1.ImageSource = new BitmapImage(new Uri(#"---\lightsonNOlaptop.jpg"));
_parentWindow.PIC.Background = b1;
}
You could get a reference to the MainWindow using the Application.Current.Windows property.
You also need to make the "PIC" Grid field internal or public to be able to access it from another window. The easiest way to do this is to set the x:FieldModifier attribute in the XAML markup.
Try this:
MainWindow.xaml:
<Grid x:Name="PIC" x:FieldModifier="public">
<Grid.Background>
<ImageBrush ImageSource="lightsofflaptop.jpg"/>
</Grid.Background>
...
Window3.xaml.cs:
private void button_Click(object sender, RoutedEventArgs e)
{
ImageBrush b1 = new ImageBrush();
b1.ImageSource = new BitmapImage(new Uri(#"---\lightsonNOlaptop.jpg"));
MainWindow mw = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
if(mw != null)
mw.PIC.Backgound = b1;
}

Access the main window datacontext from a new created window

In my MainWindow I create a new instance of a class containing different settings. After setting the parameters of the class, I set the datacontext = to that class.
public partial class MainWindow : Window
{
private MeasConSettings mMeasConSettings = new MeasConSettings();
public MainWindow()
{
InitializeComponent();
DataContext = mMeasConSettings;
}
private void MenuComm_Click(object sender, RoutedEventArgs e)
{// See code below}
}
Now I also have a function to open a new window, this window contains a textbox who's text should be bound to the datacontext of the MainWindow.
private void MenuComm_Click(object sender, RoutedEventArgs e)
{
FrmSettings newWindow = new FrmSettings();
newWindow.DataContext = mMeasConSettings;
newWindow.TxtComm.Text = mMeasConSettings.CommSettings;
newWindow.Show();
}
This code fills in the textbox from the newWindow with the right content, BUT it does not get bound propery since the datacontext does not get updated after changing the text in the textbox (TxtComm in the new created window).
An example of the XAML code for the textbox:
<TextBox Grid.Row="1" Grid.Column="3" Margin="2,0" Name="TxtComm" DataContext="{Binding Path=CommSettings, UpdateSourceTrigger=PropertyChanged}" />
"CommSettings" is a member of the MeasConsettings class
public class MeasConSettings
{
private string mCommSettings;
public string CommSettings
{
get
{
return mCommSettings;
}
set
{
mCommSettings = value;
}
}
public MeasConSettings()
{
CommSettings = "Com5:19200,8,n,1";
}
}
My problem is how can I adjust the value mMeasConSettings.CommSettings (defined in my MainWindow) in my newWindow (Which is created after pressing a button), If I change the textbox value in my newWindow, the value stored in mMeasConSettings.CommSettings should also be changed.
PS: I'm new to WPF so any advice is welcome!
As I wrote in the comment, you need to bind the Text property of your TextBox to the property of the DataContext which you want to update. Your XAML should thus be something like:
<TextBox ... Text="{Binding CommSettings, Mode=TwoWay}" />
Note that I am binding the Text property of the TextBox to the property CommSettings of your DataContext. And your C#-code for the click event should be:
private void MenuComm_Click(object sender, RoutedEventArgs e)
{
FrmSettings newWindow = new FrmSettings();
newWindow.DataContext = mMeasConSettings;
newWindow.Show();
}
We only need to set the DataContext here. Note that the DataContext is passed along to child elements, so the TextBox will have the same DataContext as its parent unless specifically set to something else.
use static property:
class Demo
{
public static string SomeSettings {get;set;}
private onLoad()
{
SomeSettings=... //Init here
}
}
In other file:
Demo.SomeSettings=....

Categories