I am trying to create a base class for my windows in a WPF application but I cannot access their members in the derived classes. For example, here is the base window:
namespace MyApp.Windows
{
public class BaseWindow : Window
{
public int MyProp { get; set; }
}
}
And here is a window:
public partial class SomeWindow : BaseWindow
{
public SomeWindow()
{
InitializeComponent();
Loaded += SomeWindow_Loaded;
}
private void SomeWindow_Loaded(object sender, RoutedEventArgs e)
{
MyProp = do something;
}
}
If I leave it like this, the MyProp proerty works just fine but I get an error that InitializeComponent() is not recognized. Therefore, in the window xaml I change the x:Class as follows:
Before
<Window x:Class="MyApp.SomeWindow"
After
<Window x:Class="MyApp.Windows.BaseWindow"
Now, InitializeComponent() doesn't give me anymore issues but MyProp suddenly isn't recognized. Why?
In case it helps, what I want is to have all windows raise an event once they are loaded (the Loaded event is fired) and I don't want to write this code for every window I have, so I thought I could write this code in a base class, derive my windows from this and, well, have everything work.
Edit: Here's all the code.
BaseWindow.cs (no other xaml):
using System.Windows;
namespace MyApp.Windows
{
public class BaseWindow : Window
{
public int MyProp { get; set; }
}
}
MainWindow.xaml.cs
namespace MyApp.Windows
{
public partial class MainWindow : BaseWindow
{
public MainWindow()
{
InitializeComponent();
}
}
}
MainWindow.xaml:
<myapp:BaseWindow x:Class="MyApp.Windows.BaseWindow"
xmlns:myapp="clr-namespace:MyApp.Windows"
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:MyApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</myapp:BaseWindow>
In order to change the base class of SomeWindow from Window to BaseWindow, you need to replace Window by BaseWindow wherever it occurs.
So
public partial class SomeWindow : Window
becomes
public partial class SomeWindow : BaseWindow
and
<Window x:Class="MyApp.Windows.SomeWindow" ...>
becomes
<myapp:BaseWindow x:Class="MyApp.Windows.SomeWindow"
xmlns:myapp="clr-namespace:MyApp.Windows" ...>
with the unavoidable XAML namespace prefix.
This is the BaseWindow class used in the example above:
namespace MyApp.Windows
{
public class BaseWindow : Window
{
public int MyProp { get; set; }
public BaseWindow()
{
Loaded += BaseWindow_Loaded;
}
private void BaseWindow_Loaded(object sender, RoutedEventArgs e)
{
}
}
}
Related
I need to open a dialog window when I open a module.
In my module I register WindowA and I want to show it in method OnInitialize() of the module. It looks like this.
public class TestModule : IModule
{
IDialogWindow _dialogWindow;
public TestModule(IContainerProvider containerProvider, IDialogWindow dialogWindow)
{
_dialogWindow = dialogWindow;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_dialogWindow.Show();
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterDialog<WindowA>();
}
}
My window
<Window x:Class="FirstModule.Views.WindowAView"
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:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:local="clr-namespace:FirstModule.Views"
mc:Ignorable="d"
Title="WindowA" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Content="Button"/>
</Grid>
</Window>
and View model for this
class WindowAViewModel : IDialogAware
{
public WindowAViewModel()
{
}
public string Title { get; }
public event Action<IDialogResult> RequestClose;
public bool CanCloseDialog()
{
return true;
}
public void OnDialogClosed()
{
}
public void OnDialogOpened(IDialogParameters parameters)
{
}
}
Also I implemented IDialogWindow
public partial class WindowAView : Window, IDialogWindow
{
public WindowAView()
{
InitializeComponent();
}
public IDialogResult Result { get; set; }
}
But instead of WindowAView a small window is shown with Width and Height equal to 0.
Could you please explain what I did wrong?
I think you misunderstood the dialog service, from the documentation:
Your dialog view is a simple UserControl that can be designed anyway you please. The only requirement it has a ViewModel that implements IDialogAware set as it's DataContext.
The dialog view is a UserControl that is shown in a dialog host. Prism uses a standard WPF window as default dialog host, but you can create your own by implementing the IDialogWindow interface and registering it. In Prism 7, there was only one dialog host for all. Since Prism 8, you can use the same dialog host for all dialog views or different dialog hosts as you want.
It's very common to be using a third-party control vendor such as Infragistics. In these cases, you may want to replace the standard WPF Window control that hosts the dialogs with a custom Window class such as the Infragistics XamRibbonWindow control.
In this case, just create your custom Window, and implement the IDialogWindow interface: [...] Then register your dialog window with the IContainerRegistry. [...] If you have more than one dialog window you would like to use as a dialog host, you register multiple dialog windows with the container by specifying a name for the window.
You inject the IDialogWindow into the module constructor, which is not your WindowAView, but simply the default dialog host window, which is empty, as there is no content to host. What you should do instead is define your view as a UserControl and adapt your WindowAViewModel accordingly.
<UserControl x:Class="FirstModule.Views.AView"
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:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:local="clr-namespace:FirstModule.Views"
mc:Ignorable="d"
Height="450"
Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Content="Button"/>
</Grid>
</UserControl>
public partial class AView : UserControl
{
public AView()
{
InitializeComponent();
}
}
class AViewModel : IDialogAware
{
public AViewModel()
{
}
public string Title { get; }
public event Action<IDialogResult> RequestClose;
public bool CanCloseDialog()
{
return true;
}
public void OnDialogClosed()
{
}
public void OnDialogOpened(IDialogParameters parameters)
{
}
}
Then register the dialog and use the IDialogService instead of IDialogWindow.
public class TestModule : IModule
{
IDialogService _dialogService;
public TestModule(IContainerProvider containerProvider, IDialogService dialogService)
{
_dialogService = dialogService;
}
public void OnInitialized(IContainerProvider containerProvider)
{
dialogService.Show(nameof(AView), new DialogParameters(), result => {});
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterDialog<AView>();
}
}
This will show your view in the default dialog window, but as stated above, you can always roll your own, if you need to. For more information, see the dialog service documentation.
I did this many times, but now it just doest work.
I am following this example:
https://www.reactiveui.net/docs/handbook/routing/
I have my MainWindow.xaml.cs
using ReactiveUI;
namespace KardexTerminal_WPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : ReactiveWindow<MainViewModel>
{
public MainWindow()
{
InitializeComponent();
}
}
}
MainWindow.xaml
<rxui:ReactiveWindow x:Class="KardexTerminal_WPF.MainWindow"
xmlns:rxui="https://reactiveui.net"
x:TypeArguments="local:MainViewModel"
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:KardexTerminal_WPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</rxui:ReactiveWindow>
MainViewModel:
using ReactiveUI;
namespace KardexTerminal_WPF
{
public class MainViewModel : ReactiveObject, IScreen
{
public MainViewModel()
{
Router = new RoutingState();
}
public RoutingState Router { get; }
}
}
Simple as documentation suggest. This is completely new project and have only ReactiveUI and ReactiveUI.WPF packages installed.
But I am getting those error:
I will be glad for any help, if anyone knows what is the issue.
EDIT 1:
If I implement IViewFor, then it works.
I have tried suggestions from comments on view usercontrol:
<rxui:ReactiveUserControl x:Class="KardexTerminal_WPF.Views.LoginView"
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:KardexTerminal_WPF.Views"
mc:Ignorable="d"
xmlns:vm="clr-namespace:KardexTerminal.ViewModels;assembly=KardexTerminal"
x:TypeArguments="vm:LoginViewModel"
xmlns:rxui="https://reactiveui.net"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
</Grid>
</rxui:ReactiveUserControl>
namespace KardexTerminal_WPF.Views
{
/// <summary>
/// Interaction logic for LoginView.xaml
/// </summary>
public partial class LoginView
{
public LoginView()
{
InitializeComponent();
}
}
}
But that makes those errors, I am not sure what is wrong:
I am using those packages:
EDIT 2:
Answer below from #mm8 helped to make the program compile.
ReactiveUI has wrong documentation and should be fixed:
https://www.reactiveui.net/docs/handbook/routing/
But there is stil error, which I cannot seem to get rid off. I will proceed with that at GitHub issue.
The namespace is http://reactiveui.net (and not https://...):
xmlns:rxui="http://reactiveui.net"
Besides this, your code-behind class should inherit from ReactiveUserControl<LoginViewModel> since this is the base class you have specified in the XAML markup:
public partial class LoginView : ReactiveUserControl<LoginViewModel>
{
public LoginView()
{
InitializeComponent();
}
}
I've got similar problems using the ReactiveUserControl class. I've found 2 workaround for this
1)Create a base class for your window that inherit from ReactiveWindow than let MainWindow inherit from that class
public class MainWindowBase : ReactiveWindow<MainViewModel> { }
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
}
Than in your xaml
<local:MainWindowBase x:Class="YourNameSpace.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:YourNameSpace"
mc:Ignorable="d"
Title="" Height="600" Width="1000" >
2)Implement manually the IViewFor<T>
public partial class MainWindow : Window, IViewFor<MainViewModel>
{
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(MainViewModel), typeof(MainWindow));
public MainViewModel ViewModel
{
get { return (MainViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (MainViewModel)value;
}
public MainWindow()
{
InitializeComponent();
}
}
EDIT: based on the comment of Glenn Watson I've updated the answer but for completeness I'll add here the previous answer that will be usefull when working on the UWP
public class MainWindowBase : ReactiveWindow<MainViewModel> { }
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow :MainWindowBase
{
public MainWindow()
{
InitializeComponent();
}
}
I have a small example github repo in which I like to open a custom ContentDialog (SpeechDialog) after a button is clicked, using the MVVMCross framework.
If I implement a ContentDialog with MVVM without a framework, the MainView will look like this:
public sealed partial class MainView : Page
{
public MainView()
{
this.InitializeComponent();
ISpeechDialogService dialog = new SpeechDialogService();
MainViewModel= new MainViewModel(dialog);
}
public MainViewModel MainViewModel{ get; set; }
}
But in MVVMCross I have an attributed MainView and I don't know how to pass the ContentDialog:
[MvxViewFor(typeof(MainViewModel))]
public sealed partial class MainView : MvxWindowsPage
{
public MainView()
{
InitializeComponent();
}
}
Some code for a better understanding:
SpeechDialogService.cs:
public class SpeechDialogService : ISpeechDialogService
{
public async Task ShowAsync()
{
var contentDialog = new Speech();
await contentDialog.ShowAsync();
}
}
directlink to the Speech.xaml
TL;DR
Is my approach right? If yes, how can I pass a ContentDialog to the MainViewModel? If not, how to implement a ContentDialog with MVVMCross?
I think you can use ViewModelLocator here and use MVVM pattern, regardless of the framework. See sample implementation.
public class ViewModelLocator
{
public MainPageViewModel MainPageViewModel
{
get
{
ISpeechDialogService dialogService = new SpeechDialogService();
MainPageViewModel vm = new MainPageViewModel(dialogService);
return vm;
}
}
}
Here, you can use Autofac to resolve your ViewModels' dependecies, also make the service singleton https://autofaccn.readthedocs.io/en/latest/resolve/index.html
Then in your App.xaml, add a resource for locator:
<Application.Resources>
<services:ViewModelLocator x:Key="Locator" />
</Application.Resources>
Then in your page (preferably not in code behind) you should assign your DataContext like so:
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
d:DesignHeight="450" d:DesignWidth="800"
DataContext="{Binding MainPageViewModel, Source={StaticResource Locator}}">
</Page>
Then your ViewModel should look like:
using App1.Services;
namespace App1.ViewModels
{
public class MainPageViewModel
{
private readonly ISpeechDialogService _speechDialogService;
public MainPageViewModel(ISpeechDialogService speechDialogService)
{
_speechDialogService = speechDialogService;
}
}
}
Your dialog service look like:
using System.Threading.Tasks;
namespace App1.Services
{
public class SpeechDialogService : ISpeechDialogService
{
public async Task ShowAsync()
{
var contentDialog = new Speech();
await contentDialog.ShowAsync();
}
}
}
Hope this helps.
Im trying to create a list of wpf user controls and then set the mainwindows content to the usercontrol at runtime.
Since all UserControls will also share interfaces I want to build them from a derived window class as such:
public class BaseControl : UserControl, IControlState
public string ControlName
{
get
{
return this.GetType().Name;
}
}
public void UseState(object parameters)
{
throw new NotImplementedException();
}
}
Class derived from UserControl
public partial class Buglist : UserControl
{
public Buglist()
{
InitializeComponent();
}
}
Class derived from my BaseControl
public partial class Login : BaseControl
{
public Login()
{
InitializeComponent();
}
}
The Login xaml to allow Visual Studio designer to recognize a designable component
<MyType:BaseControl x:Class="BugAnalyzer.Controls.Login"
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:BugAnalyzer.Controls"
xmlns:MyType="clr-namespace:BugAnalyzer.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="112,72,0,0" TextWrapping="Wrap" Text="login" VerticalAlignment="Top"/>
</Grid>
In my main window I want to be able to add these controls to a list and then be able to access shared properties such as ControlName.
public partial class MainWindow : Window
{
List<UserControl> uclist = new List<UserControl>();
List<BaseControl> bcList = new List<BaseControl>();
public MainWindow()
{
InitializeComponent();
Buglist UserControlDerivedWindow = new Buglist();
Login BaseControlDerivedWindow = new Login();
uclist.Add(UserControlDerivedWindow); //Works
bcList.Add(BaseControlDerivedWindow); //Fails
}
why can I add the class derived from UserControl to a list UserControls
but not a class derived from BaseControl to a list of BaseControls?
Strangely I have solved this though I not sure what the root issue was
For clarity in the question I wrote
Login BaseControlDerivedWindow = new Login();
but the actual code was
Login login = new Login();
which caused the compiler to issue something like "
Cannot covert from Program.Controls.Login to Program.Controls.BaseControl"
after I changed the code for clarity it compiled and then let me rename the Login instance back to login and compiled no issues again.
I have a simple class called CustomPage which inherits from Window:
public class CustomPage : Window
{
public string PageProperty { get; set; }
}
I want my MainWindow codebehind to inherit from CustomPage as follows:
public partial class MainWindow : CustomPage
{
public MainWindow()
{
InitializeComponent();
}
}
Unfortunately, I get the following error:
Partial declarations of 'WpfApplication1.MainWindow' must not specify different base classes
I can set x:Class to "WpfApplication1.CustomPage" in MainWindow.xaml, but then it appears I don't have access to the UI elements defined in MainWindow.xaml...
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
}
public class CustomPage : Window
{
public string PageProperty { get; set; }
}
<myWindow:CustomPage x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myWindow="clr-namespace:WpfApplication4"
Title="MainWindow" Height="800" Width="800">
<Grid>
</Grid>
</myWindow:CustomPage>
I hope this will help.
You need to update your xaml like this -
<local:CustomPage x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1">
</local:CustomPage>
Here, local is your namespace where CustomPage and MainWindow resides.
As the error suggested, you can't declare different base classes for partial declaration of class. So, in XAML too, you need to use the CustomPage instead of WPF Window