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";
}
}
Related
I am trying to migrate one of my macOS app to Windows UWP.
There is a requirement that I need to call the function in another class
namespace MyApp
{
public sealed partial class MainPage : Page
{
public bool isOk;
public MainPage()
{
}
public void doSomething(){
};
/*
public static void doSomething(){
isOk=false;// isOk is inaccessible
};*/
}
public sealed partial class AnotherPage : Page
{
public AnotherPage()
{
//call doSomething() in MainPage
}
}
}
it is very easy to implement this in objective-c via protocol(interface)
but in c#, the mechanism of the interface is different from the objective-c protocol.
of course, I can use the code below
MainPage mainPage=new MainPage();
mainPage.dosomthing();
but I wonder if this is valid for Page object related to the XAML file.
or there is a common usage to call the function in another class?
Your comment welcome
NOTE: This is an answer to your question but from a design perspective this is bad.
You need a reference to the instantiated MainPage. I'm sure there's one in the framework but for example just so this can be understood let's make a public static class to hold this reference.
public static class Global
{
public static Page MainPage { get; set; }
}
Now in the MainPage constructor assign itself to this property of the static Global class.
public MainPage()
{
Global.MainPage = this;
}
Now from any other page you can access it.
public AnotherPage()
{
Global.MainPage.doSomething();
}
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
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.
I have several UserControls that are sharing some common properties. Example:
private List<MyObject> Sample
{
get
{
return Session["MyObject"] as List<MyObject>;
}
set
{
Session["MyObject"] = value;
}
}
I want to share this to all user controls inside my project. (Not to other projects in a solution, of course). What I'm trying to do is create a separate class and inherit from that class. Something like:
public class SampleBase : Web.UI.UserControl
{
protected List<MyObject> Sample
{
get
{
return Session["MyObject"] as List<MyObject>;
}
set
{
Session["MyObject"] = value;
}
}
}
And then my control can inherit those values by deriving from that class:
partial class myControl : SampleBase
One problem I encounter is that I cannot derive from base if control already has something inherited:
partial class myControl : SomethingELSE
Otherwise it works fine, but I'm not sure if it is a good approach and I'm looking for suggestions.
If my understanding is correct, you only want to get rid of the inheritance hierarchy of your User Controls
Another approach would be using Extension Methods
For example:
Interface to mark your USerControls
public interface IMyUserControlMark { }
Extensions
public static class MyUserClassExtensions
{
public static List<object> GetSampleData(this IMyUserControlMark myUserControl)
{
if (HttpContext.Current.Session["MyObject"] == null)
{
return Enumerable.Empty<object>().ToList();
}
return HttpContext.Current.Session["MyObject"] as List<object>;
}
public static void SetSampleData(this IMyUserControlMark myUserControl, List<object> myObject)
{
HttpContext.Current.Session["MyObject"] = myObject;
}
}
User control
public partial class Content1 : System.Web.UI.UserControl, IMyUserControlMark
{
...
}
public partial class Content2 : System.Web.UI.UserControl, IMyUserControlMark
{
....
}
Now you will be able to call your extension methods from within your UserControl or from the ASPX code behind like this:
From the UserControl
var myObject = this.GetSampleData();
this.SetSampleData(myObject);
From the ASPX code behind
var myObject = this.uc1.GetSampleData();
this.uc1.SetSampleData(myObject);
This is a classic example where you need to "favor composition over inheritance".
Instead of inheriting from the class, you hold a reference to an instance of the class. Then you provide simple pass-through code to access the methods/properties of the class.
So, for your example:
public class SomeBehavior
{
public List<MyObject> Sample
{
get { return Session["MyObject"] as List<MyObject>; }
set { Session["MyObject"] = value; }
}
}
public class MyControl : UserControl
{
private SomeBehavior _someBehavior;
public MyControl()
{
_someBehavior = new SomeBehavior();
}
public List<MyObject> Sample
{
get { return _someBehavior.Sample; }
set { _someBehavior.Sample = value; }
}
}
Another option is to allow access to the behavior class directly:
public class MyControl : UserControl
{
public SomeBehavior SomeBehavior { get; private set; }
public MyControl()
{
SomeBehavior = new SomeBehavior();
}
}
The advantage of this is that you don't have to write the pass-through code. The disadvantage is that it violates the Law of Demeter, which says that you should "only talk to your immediate friends". If you do it this way, other classes that use MyControl need to know about SomeBehavior. Following the Law Of Demeter can improve maintainability and adaptability of your code, but it comes at a cost of lots of pass-through code.
Apart from previous solutions, maybe it's time for applying some MVC/MVP pattern?
For web forms there is a great framework called WebFormsMVP: link
In the library there is a mechanism called Cross Presenter Messaging thanks to which you can share a data between your controls using the publish/subscribe pattern.
For better explanation look here and here
I suggest to give the library a chance :)
In C# you can inherit from only one class and implement multiple interfaces.
This is allowed:
partial class myControl : SampleBase
partial class myControl : SampleBase, Interface1
partial class myControl : SampleBase, Interface1, Interface2, Interface3
This is NOT allowed:
partial class myControl : SomethingELSE, SampleBase
Try making SomethingELSE inherit from SampleBase if it satisfies your design. If not, then I suggest encapsulating SampleBase as a property of each control that needs it as it also suggested #DanM.
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.