I have some trouble with the MVVM cross declarations. Everytime i change the view from ContentPage to MvxContentPage it shows this declaration error.
public partial class TestView: ContentPage
{
public TestView()
{
InitializeComponent();
BindingContext = new TestViewModel();
}
}
to this;
public partial class TestView: MvxContentPage<TestViewModel>
{
public TestView()
{
InitializeComponent();
}
}
XAML;
<views:MvxContentPage xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
it shows me "Partial Declarations of TestView must not specify different base classes". I already tried all solutions from preview post.
Make sure that you have specified the correct type argument and that you don't have any other class with the same name:
<views:MvxContentPage x:TypeArguments="viewModels:TestViewModel"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:YourProject.ViewModels;assembly=YourProject"
xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
x:Class="YourProject.Forms.UI.Pages.TestView">
...
It sounds like you have another file somewhere in your project that has the line
public partial class TestView : ContentPage
It could be in some other folder, but still have the same namespace (particularly if you've been doing cuts-and-pastes of files).
Related
I am trying to create a form that inherits from a form. The base class is called Window, and it inherits from MetroForm, which is a NuGet package that I've downloaded and installed on the project. This form loads without issue in the Designer.
public partial class Window : MetroForm
{
public WindowType windowType { get; }
public Engine engine { get; }
public Window(WindowType windowType, Engine engine)
{
InitializeComponent();
this.windowType = windowType;
this.engine = engine;
}
}
The child form is declared and constructed as follows:
public partial class GraphicsWindow : Window
{
public GraphicsWindow(WindowType windowType, Engine engine) : base(windowType, engine)
{
InitializeComponent();
}
}
It throws the following error in the designer window:
"Constructor on type 'DispatcherDesk.Windows.Window' not found."
I've found multiple threads that address this error under the exact same circumstances, but none of the solutions are working for me. I've tried adding parameterless constructors to both the parent and child forms, adding InitializeComponent(); calls to them, and using the [Obsolete] attribute and private access modifier. I've also tried rebuilding the solution and restarting Visual Studio at each step.
All I can figure at this point is that these solutions no longer work in VS2017, but I don't know why. Thanks in advance for any answers!
My question involves something where thousand and one topics are created about,
and if I overlooked an answer to my question, I'm sorry, but as far as I looked none really could answer my question. For example:
Opening new window in MVVM WPF
The answers are okay when if you use just one WPF project (including models, vms and views) but since I'm learning how to implement MVVM the correct way (and I've read multiple times that best practice is to create seperate class lib (dll's) for model, viewmodel and a seperate gui project) that doesn't seem to work in my eyes because if I want to create an interface like IWindowService (described on previous url and also here, It's impossible to access Window or Control class because then I should have a reference to the gui project and the whole goal of the pattern is wrecked.
So my question is how to show a new window (with a new viewmodel) from for example the MainViewModel, while respecting the loosely coupled MVVM principles AND seperate projects.
More in depth example of what I'm trying to achieve:
I have following structure:
MODEL (dll project)
Profile
VIEWMODEL (dll project)
MainViewModel
AddProfileViewModel
VIEW (WPF) (exe project)
MainWindow
AddProfileWindow
I open MainWindow and I want to press the button AddProfile, then AddProfileWindow needs to show up with the AddProfileViewModel attached to it.
Define the IWindowService interface in the model project.
Reference the model project from the view model project.
Implement the IWindowService in the WPF application (view) project.
The button should then be bound to an ICommand that uses IWindowService to open the window. Something like this:
public class MainWindowViewModel
{
private readonly IWindowService _windowService;
public MainWindowViewModel(IWindowService windowService)
{
_windowService = windowService;
AddProfile = new DelegateCommand(() =>
{
_windowService.OpenProfileWindow(new AddProfileViewModel());
});
}
public ICommand AddProfile { get; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel(new WindowService());
}
}
public class WindowService : IWindowService
{
public void OpenProfileWindow(AddProfileViewModel vm)
{
AddProfileWindow win = new AddProfileWindow();
win.DataContext = vm;
win.Show();
}
}
I was struggling with this and found the answer posted by #mm8 very helpful as well as a few other posting. I wasn't crazy about the idea of having to create a class (or method within a class) for each view, so I created my own variation. This is one of my first projects with WPF while attempting to use MVVM, so I'm sharing this in case it is helpful, and also to get feedback from the more experienced folks.
public class WindowDialogService : IWindowDialogService
{
private static readonly Dictionary<Type, Type> viewModelPairs =
new Dictionary<Type, Type>
{
[typeof(DetailsViewModel)] = typeof(DetailsView)
};
public void ShowWindowDialog(IViewModelBase viewModel)
{
if (!viewModelPairs.TryGetValue(viewModel.GetType(), out Type viewType))
{
throw new ArgumentException("View Model not mapped", nameof(viewModel));
}
if (viewType.GetConstructor(Type.EmptyTypes).Invoke(new object[] { }) is Window view)
{
view.DataContext = viewModel;
view.Owner = Application.Current.MainWindow;
view.ShowDialog();
}
}
}
New to Caliburn and WPF MVVM, so I may be overlooking something pretty simple and I couldn't find anything searching the web.
Set up a simple wpf project with Caliburn.Micro. Set window title in ShellView.xaml. Works fine. Main MetroWindow displays the title as expcted.
Works fine:
[Export(typeof (IShell))]
public class ShellViewModel : PropertyChangedBased, IShell
{}
But change to:
[Export(typeof (IShell))]
public class ShellViewModel : Conductor<object>
{}
and Window title is the fully qualified name of this ViewModel. ANy help would be appreciated. Thanks.
You can use it like this:
[Export(typeof (IShell))]
public class ShellViewModel : Conductor<IScreen>
{
public ShellViewModel()
{
DisplayName = "Your window title";
}
}
In my repository you can find some applications in WPF using Caliburn.Micro, for example:
DanceFloor - this one uses Conductor
Library-Manager
Thanks to Wojciech for pointing me in the right direction.
When ShellViewModel is inheriting PropertyChangeBase and IShell, setting the Title = "Window Title" in ShellView.xaml works. But, when using Caliburn.Micro 2.0.2 and inheriting from Conductor (single screen conductor) the window title is overwritten with the fully-qualified name of the view model (in my case):
FBAGOLDEVALUATOR.APP.VIEWMODELS.SHELLVIEWMODEL
This looks like bug in Caliburn.Micro v2.02, unless I'm missing something.
The workaround: Bind the Title property of the window in .xaml to a public property in the ViewModel. The .xaml line:
Title="{Binding Path=DisplayTitle, Mode=OneWay}"
The property in ShellViewModel.cs:
private string _displayTitle;
public String DisplayTitle
{
get
{
return _displayTitle;
}
set
{
if (value.Equals(_displayTitle)) return;
_displayTitle = value;
NotifyOfPropertyChange(() => DisplayName);
}
}
Then set it in the ShellViewModel constructor:
DisplayTitle = "FBA Gold Evaluator";
That seems to work.
I'm new to Caliburn.Micro (and MVVM for that matter) and I'm trying to Activate a screen with my conductor located in ShellViewModel from a button within a sub-viewmodel (one called by the conductor). All the tutorials I've seen have buttons in the actual shell that toggle between so I'm a little lost.
All the ViewModels share the namespace SafetyTraining.ViewModels
The ShellViewModel (first time ever using a shell so I might be using it in the wrong manner)
public class ShellViewModel : Conductor<object>.Collection.OneActive, IHaveDisplayName
{
public ShellViewModel()
{
ShowMainView();
}
public void ShowMainView()
{
ActivateItem(new MainViewModel());
}
}
ShellView XAML
<UserControl x:Class="SafetyTraining.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel>
<ContentControl x:Name="ActiveItem" />
</DockPanel>
MainViewModel - the main screen (does correctly display).
public class MainViewModel : Screen
{
public void ShowLoginPrompt()
{
LoginPromptViewModel lg = new LoginPromptViewModel();//This does happen
}
}
MainView XAML
<Button cal:Message.Attach="[Event Click] = [ShowLoginPrompt]">Login</Button>
LoginPromptViewModel
public class LoginPromptViewModel : Screen
{
protected override void OnActivate()
{
base.OnActivate();
MessageBox.Show("Hi");//This is for testing - currently doesn't display
}
}
EDIT Working Code:
Modified Sniffer's code a bit to properly fit my structure. Thanks :)
var parentConductor = (Conductor<object>.Collection.OneActive)(this.Parent);
parentConductor.ActivateItem(new LoginPromptViewModel());
You are doing everything correctly, but you are missing one thing though:
public void ShowLoginPrompt()
{
LoginPromptViewModel lg = new LoginPromptViewModel();//This does happen
}
You are creating an instance of LoginPromptViewModel, but you are not telling the conductor to activate this instance, so it's OnActivate() method is never called.
Now before I give you a solution I should suggest a couple of things:
If you are using the MainViewModel to navigate between different view-models then it would be appropriate to make MainViewModel a conductor itself.
If you aren't using it like that, then perhaps you should put the button that navigates to the LoginPromptViewModel in the ShellView itself.
Now back to your problem, since your MainViewModel extends Screen then it has a Parent property which refers to the Conductor, so you can do it like this:
public void ShowLoginPrompt()
{
LoginPromptViewModel lg = new LoginPromptViewModel();//This does happen
var parentConductor = (Conductor)(lg.Parent);
parentConductor.Activate(lg);
}
I want to attempt an MVC design for my little app.
I have a normal Csharp class ViewBase which extends UserControl. It's a single .cs file.
I have multiple classes that I want to extend ViewBase. These are actual UserControls so they have a code behind .cs file and a .xaml file.
However, CSharp tells me that for these classes, their base class "differs from declared in other parts".
Is what I want to do possible at all? What am I doing wrong?
Note that I did not modify my XAML files, so they still use tags.
Here is the relevant code:
// This gives the error in question and ViewBase is underlined
// "Base class of LoginView differs from declared in other parts"
public partial class LoginView : ViewBase {
public LoginView(Shell shell, ControllerBase controller) : base(shell, controller) {
InitializeComponent();
}
}
// This one is a single .cs file
public abstract class ViewBase : UserControl {
public Shell Shell { get; set; }
public ControllerBase Controller { get; set; }
protected ViewBase(Shell shell, ControllerBase controller)
{
Shell = shell;
Controller = controller;
}
}
Note that I did not modify my XAML
files, so they still use tags
That's your problem. You'll need to change:
<UserControl ...>
...
</UserControl>
to
<local:ViewBase xmlns:local="clr-namespace:..."
...
</local:ViewBase>
The problem is you're telling the compiler you're inheriting ViewBase in one place (the .cs file) and UserControl in another (the .xaml file).