I currently have a simple WPF application, in the MainWindow I will have a variable (In this case the variable is a class that holds data). Then I have a User Control which has the same variable.
Currently, I'm passing the variable with the ref keyword and it works perfectly fine, however, is this save/good practice? Is there a better way of linking this two variables together?
I am aware of the existence of DependencyProperty, however, I could not get it to work.
MainWindow:
public partial class MainWindow : Window
{
private TestClassWithInfo m_SelectedInfo;
public MainWindow()
{
InitializeComponent();
m_SelectedInfo = new DrawingInformation();
TestGridUC mp = new TestGridUC(ref m_SelectedInfo);
TestCanvas.Childrens.Add(mp);
}
}
TestGridUI:
public partial class TestGridUC : UserControl {
private TestClassWithInfo m_SelectedInfo;
public TestGridUC (ref TestClassWithInfo e)
{
InitializeComponent();
m_SelectedInfo = e;
}
}
TestClassWithInfo:
public class TestClassWithInfo
{
public Image imageTest;
public int intTest;
public TestClassWithInfo ()
{
m_img = null;
m_layer = 0;
}
}
I am aware of the existence of DependencyProperty, however, I could not get it to work.
A dependency property really is the way to go about it though:
public partial class TestGridUC : UserControl
{
public TestGridUC()
{
InitializeComponent();
}
public TestClassWithInfo Info
{
get { return (TestClassWithInfo)GetValue(InfoProperty); }
set { SetValue(InfoProperty, value); }
}
public static readonly DependencyProperty InfoProperty =
DependencyProperty.Register("Info", typeof(TestClassWithInfo), typeof(TestGridUC),
new PropertyMetadata(null /*or initialize to a default of new TestClassWithInfo()*/ ));
}
Now you can bind to that property from the xaml in your MainWindow:
<local:TestGridUC
Info="{Binding Info}"></local:TestGridUC>
If you need help with that part, as pr177 answered there are many tutorials on getting started with WPF with the MVVM pattern. The basics here would involve a view model object that contains a TestClassWithInfo public property that you bind to.
Have a look at the MVVM (Model-View-ViewModel) Pattern
There are many tutorials & introductions like that:
https://blogs.msdn.microsoft.com/ivo_manolov/2012/03/17/model-view-viewmodel-mvvm-applications-general-introduction/
or
https://social.technet.microsoft.com/wiki/contents/articles/32164.wpf-mvvm-step-by-step-2.aspx
Related
I want to update textblock1 to 'there' from a class other than the main. As shown this code gives no errors, but does not work. Textblock1 is set to
FieldModifier="Public".
namespace myProgram
{
public sealed partial class MainPage : Page
{
Library stuff = new Library();
public MainPage()
{
this.InitializeComponent();
}
public void mainStuff()
{
stuff.Doit(new MainPage());
}
}
}
namespace myProgram
{
public class Library
{
public void Doit(MainPage mainPage)
{
mainPage.textblock1.Text = "there";
}
}
}
The short answer
Pass your Window as a parameter to the other class.
public class Library {
public void Doit(MainPage mainPage)
{
mainPage.textblock1.Text = "there";
}
}
EDIT According to the other answer that is posted here, you can't (by default) access controls from outside the class (as they are set to protected).
You can override the access modifier to public (refer to the other answer), but that seems to violate the idea that UI and data logic should be separated.
It does work, from a technical point of view; I just suggest not doing it because it can lead to future problems in development.
The cautionary answer
You should endeavour to keep your UI and data logic separate. In most cases where you want another class to access your window's controls; you are violating the principle of UI and data logic segregation.
In short, you don't want anyone (except MainPage) to be aware that a Mainpage has a Textblock; let alone giving them the ability to set its content directly.
In MainPage, create a public property:
public String MyDisplayValue
{
get
{
return this.textblock1.Text;
}
set
{
this.textblock1.Text = value;
}
}
In your external Library class:
public void Doit(MainPage mainPage)
{
mainPage.MyDisplayValue = "there";
}
Functionally speaking, the application works the same as in the short answer I supplied. But this one creates a better separation between UI and data logic.
This is a matter of good practice, not technical requirement.
Make you TextBlock control public like this. x:FieldModifier="public"
<TextBlock Name="TextBlockName"
x:FieldModifier="public"/>
Then expose you MainPage class
public sealed partial class MainPage : Page
{
public static MainPage mainPage { get; set; }
public MainPage()
{
this.InitializeComponent();
mainPage = this;
}
}
then in our Library class do this:
public class Library
{
private TextBlock myTb{ get; set; }
public Library()
{
myTb = MainPage.mainPage.TextBlockName;
}
public void Doit()
{
myTb.Text = "there";
}
}
I have rewriting app into MvvM and having a problem with calling one function from viewModel. That function takes as parameter a window it should alter.
I code behind it looks like this:
this.utils = new IntroPageUtils(this);
this.Dispatcher.BeginInvoke(new Action(this.utils.InitializeWizard), null);
where
this = window which should be altered
InitializeWizard the method we are calling to make it
In code behind its quite easy but how how to achieve this in viewModel?
at this moment code behind file is clean:
public partial class IntroPage : Page
{
/// <summary>
/// Initializes a new instance of the IntroPage class.
/// </summary>
public IntroPage()
{
InitializeComponent();
}
}
and a viewModel:
class IntroPageViewModel: ObservableObject, INavigable
{
private ICommand nextButtonCommand;
private ICommand cancelButtonCommand;
private IntroPageUtils utils;
public IntroPageViewModel()
{
this.utils = new IntroPageUtils(/*What to put here? */);
this.Dispatcher.BeginInvoke(new Action(this.utils.InitializeWizard), null); // There is not such thing as dispatcher here
}
/* Rest of a viewModel */
}
I'll be grateful for any samples, guides or tutorials how to overcome this problem.
A ViewModel is set as Datacontext of a View.
For example :
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel(this);
}
}
In this example, we pass the window in parameter to the view model.
So you can store the reference to the window in the view model.
class MainViewModel : ViewModelBase
{
#region fields
private MainWindow mainWindow;
#endregion
#region properties
public MainWindow MainWindow
{
get
{
return this.mainWindow;
}
}
#endregion
public MainViewModel(MainWindow mainWindow)
{
this.mainWindow = mainWindow;
}
}
I guess it depends on the answer to a few questions. Could there be more than one instance of the IntroPageViewModel in your application? Would these be specific to the window it's view is hosted in? If so (and I assume so), why not pass in your IntroPage instance into the constructor of the view model and then use that?
The view model then has a reference to the model it represents, which is perfectly valid I think.
i.e.
View Model:
class IntroPageViewModel : ObservableObject, INavigable
{
private readonly IntroPage _model;
private readonly IntroPageUtils _introPageUtils;
public IntroPageViewModel(IntroPage model)
{
_model = model;
_introPageUtils = new IntroPageUtils(Window.GetWindow(_model));
}
}
Wherever you initialise the view model instance:
IntroPage introPage = new IntroPage();
IntroPageViewModel viewModel = new IntroPageViewModel(introPage);
In my prism application I want to make a single shared instance of a view. When I try to navigate the first time it works fine, but when I try to second time it's not working. If I change the PartCreationPolicy from Shared to NonShared it works but it's give me a new instance. Are there any options for another way to do this?
[Export(ViewNames.AppView)]
[PartCreationPolicy(CreationPolicy.Shared)]
public partial class AppMain : UserControl
{
public AppMain()
{
InitializeComponent();
}
}
You might want to play around with Prism's KeepAlive value for your view. This value determines whether the view should be removed from the region when you navigate away from it. You have two ways of doing this:
Using the RegionMemberLifetime attribute
[RegionMemberLifetime(KeepAlive = false)]
[Export(ViewNames.AppView)]
[PartCreationPolicy(CreationPolicy.Shared)]
public partial class AppMain : UserControl
{
public AppMain()
{
InitializeComponent();
}
}
Implementing the IRegionMemberLifetime interface
[Export(ViewNames.AppView)]
[PartCreationPolicy(CreationPolicy.Shared)]
public partial class AppMain : UserControl, IRegionMemberLifetime
{
public AppMain()
{
InitializeComponent();
}
public bool KeepAlive
{
get { return false; }
}
}
You can read some more about the KeepAlive property here.
If I try "var mainpage new Mainpage()"
I will run the mainpage constructor and then all the fields in the XAML object will return to null. How to I access XAML objects in silverlight that are from a different class but part of the same namespace?
Let me explain by example. If you look at the first answer, here is what I am encountering
public class MyPage
{
MyPage()
{
// the constructor makes all the variables from the xaml null
}
public TextBox MyTextBox
{
get { return SomeTextBox; }
}
}
public class SomeOtherClass
{
private void SomeFunction()
{
var page = new MyPage(); // this makes the text empty
var sometext = page.MyTextBox.Text; // so sometext will be empty
}
}
So whatever the user imputs when the program first runs turns to null when I run SomeFunction.
What I am first going to try is to see if when SomeClass is created, the values are put into that class.
If that fails, I am going to try MVVM. I have seen the http://www.vimeo.com/8915487 video and I got the sample mvvm code
Here is the Model:
namespace SimpleMVVM.Model
{
public class SimpleModel
{
// super easy version
//public string SomeSimpleValue { get; set; }
private string _SomeSimpleValue = string.Empty;
// actually do something version...
public string SomeSimpleValue
{
get
{
return "some value";
}
set
{
_SomeSimpleValue = value;
}
}
}
}
here is the view:
and here is the viewmodel.cs
using Simple;
using SimpleMVVM.Model;
namespace SimpleMVVM.ViewModel
{
public class SimpleViewModel : SimpleViewModelBase
{
private SimpleModel MyModel = new SimpleModel();
public string SomeSimpleValue
{
get { return MyModel.SomeSimpleValue; }
set
{
if (MyModel.SomeSimpleValue != value)
{
MyModel.SomeSimpleValue = value;
RaisePropertyChanged("SomeSimpleValue");
}
}
}
}
}
Using this example, I am wondering if it will just as easy as injecting a ViewModel and then changing the bindings in the Model and the View.
Is MVVM really this easy?
There is one more. It is the viewmodel base class
using System.ComponentModel;
namespace Simple
{
public class SimpleViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string PropertyName)
{
var e = new PropertyChangedEventArgs(PropertyName);
PropertyChangedEventHandler changed = PropertyChanged;
if (changed != null) changed(this, e);
}
}
}
OK, so now the hard part. If I create a new class. How do I get the data from the viewmodel class?
First, let me get this rant out of the way: what you propose is very bad design. It fits the definition of smelly code.
If you insist on doing it this way, the "best" approach to take is to declare some public variables on your page that return the actual UI elements.
<UserControl x:Class="MyNamespace.MyPage" ...>
<Grid>
<TextBox x:Name="SomeTextBox" Width="100" />
</Grid>
</UserControl>
public class MyPage
{
public TextBox MyTextBox
{
get { return SomeTextBox; }
}
}
public class SomeOtherClass
{
private void SomeFunction()
{
var page = new MyPage();
page.MyTextBox.Text = "some text";
}
}
Of course the preferred method would be to use something like the MVVM pattern to implement binding from your window to its viewmodel, then you can just read the property values from the viewmodel, this way you avoid trying to touch any UI elements from a totally different class.
Another way to do it (without going the full MVVM route) is to inject the necessary values into the constructor of the control/page that you are instantiating, and from there you can assign them to the appropriate UI element properties. This is still smelly, but better than directly accessing the UI elements from the outside.
I try to rewrite my Application using the MVVM pattern.
I have a window to show related documents for different objects with static methods like this:
public partial class ShowRelatedDocuments : Window
{
private ShowRelatedDocuments()
{
InitializeComponent();
}
public static void ShowRelatedDocument(A objA)
{
ShowRelatedDocuments srd = new ShowRelatedDocuments();
srd.HandleA(objA);
srd.ShowDialog();
}
public static void ShowRelatedDocument(B objB)
{
ShowRelatedDocuments srd = new ShowRelatedDocuments();
srd.HandleB(objB);
srd.ShowDialog();
}}
Is there a way to keep these methods static like this?
ShowRelatedDocumentsVM.ShowRelatedDocument(A objA);
ShowRelatedDocumentsVM.ShowRelatedDocument(B objB);
I didn't find anything about ViewModels and static methods. Can a VM create a instance of itself and show his View (here a window)?
Or is the better way to pass the objects as parameter to the constructor of the VM like this?
public ShowRelatedDocumentsVM(A objA)
{
HandleA(obj A)
ShowRelatedDocuments srd = new ShowRelatedDocuments();
srd.DataContext = this;
srd.ShowDialog();
}
public ShowRelatedDocumentsVM(B objB)
{
HandleB(objB);
ShowRelatedDocuments srd = new ShowRelatedDocuments();
srd.DataContext = this;
srd.ShowDialog();
}
Or are both ways wrong, cause i breach the MVVM pattern due creating the view in the viewmodel?
Thx in advance.
How to display dialogs is one of the areas of MVVM that is not immediately clear, and there are a number of ways the behaviour can be achieved.
I would suggest using either a mediator (as described here) or by injecting a dependency on the view model that controls dialogs:
interface IDialogService
{
void ShowRelatedDocumentsA(A a);
}
...
class MyViewModel
{
private IDialogService _dialogService
public MyViewModel(IDialogService dialogService) { _dialogService = dialogService; }
public void DoSomething()
{
_dialogService.ShowDialog(...);
}
}
Either of these can will permit you to control the creation of the view outside of the view model, and will remove any explicit references from VM -> V.