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);
}
}
Related
I am a developer with Xamarin.
I'm developing with Xamarin.Forms.
I would like to open a picker when a page transition occurs.
I searched for existing information and found the following information about opening the picker by using Focus().
<ContentPage
Appearing="OnContentPageAppearing"
>
<Picker x:Name="TypePicker"
ItemsSource="{Binding ItemTypes}"
SelectedItem="{Binding SelectedItemType}" />
private void OnContentPageAppearing(object sender, EventArgs e)
{
TypePicker.Focus();
}
However, this is not enough to open the Picker.
Do we need the equivalent of a Click or Tap event?
If so, how do I write the code?
You could use Rg.Plugins.Popup plugin to create a popup page with picker.
I am not sure how do you use the navigation. I use a button click event to open the pop up page first. And then when i close the pop up page, navigate to the another page.
The code of navigating to the pop up page.
private async void Button_Clicked(object sender, EventArgs e)
{
await PopupNavigation.Instance.PushAsync(new Popup_picker());
}
Popup page:
Xaml:
<rg:PopupPage xmlns:rg="http://rotorgames.com" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App3.Popup_picker">
<rg:PopupPage.Animation>
<rg:ScaleAnimation
DurationIn="400"
DurationOut="300"
EasingIn="SinOut"
EasingOut="SinIn"
HasBackgroundAnimation="True"
PositionIn="Center"
PositionOut="Center"
ScaleIn="1.2"
ScaleOut="0.8" />
</rg:PopupPage.Animation>
<StackLayout Margin="60"
BackgroundColor="White">
<Picker x:Name="TypePicker"
ItemsSource="{Binding ItemTypes}"
ItemDisplayBinding="{Binding Item}"
SelectedItem="{Binding SelectedItemType}" Unfocused="TypePicker_Unfocused"/>
</StackLayout>
</rg:PopupPage>
ViewModel for the Popup page:
public class PickerViewModel
{
public IList<PickerModel> ItemTypes { get; set; }
public PickerModel SelectedItemType { get; set; }
public PickerViewModel()
{
ItemTypes = new List<PickerModel>()
{
new PickerModel() { Item="A"},
new PickerModel() { Item="B"},
new PickerModel() { Item="C"},
};
SelectedItemType = ItemTypes[0];
}
}
public class PickerModel
{
public string Item { get; set; }
}
Code behind: Do the navigation when you close the popip page.
public partial class Popup_picker : PopupPage
{
public Popup_picker()
{
InitializeComponent();
this.BindingContext = new PickerViewModel();
}
protected override Task OnAppearingAnimationBeginAsync()
{
return Content.FadeTo(1);
}
protected override Task OnAppearingAnimationEndAsync()
{
return Content.FadeTo(1);
}
private async void TypePicker_Unfocused(object sender, FocusEventArgs e)
{
await PopupNavigation.Instance.PopAsync();
}
protected async override void OnDisappearing()
{
base.OnDisappearing();
await Navigation.PushAsync(new Page1());
}
}
For more details about the Rg.Plugins.Popup, please refer to the link below. https://github.com/rotorgames/Rg.Plugins.Popup
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.
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);
}
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>
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=....