I'm converting our old program written in VB6 to C# WPF, and there is this code that I'm having a hard time trying to convert.
In our old VB6 project. The code is written like this:
Select Case paymentType
Case "CASH":
Set dlgpayment = New dlgPaymentCash
dlgPaymentCash.lblChange = format(-gvAmountDue - gvAddOnAmount, "0.00") '/NJ-2011/10/25 Added gvAddOnAmount
Case "CARD":
If PaymentTypeSupports(gvPaymentType, "multiple") And PaymentTypeSupports(gvPaymentType, "remember") And TypeName(dlgpayment) = "dlgPaymentCreditCard" Then
Else
Set dlgpayment = New dlgPaymentCreditCard
End If
dlgPayment is declared as:
Window dlgPayment = new Window();
so whenever I want to inherit other windows, I will just simply code like this:
Set dlgPayment = new dlgPaymentCash //dlgPaymentCash is a form.
And whenever I do this, I was able to access the controls in the form dlgPaymentCreditCard like dlgPaymentCash.lblChange, but it doesn't work in C#.
This is roughly the conversion:
switch (paymentType) {
case "CASH":
var dlgpayment = new dlgPaymentCash();
dlgPaymentCash.lblChange = string.Format("{0:0.00}", -gvAmountDue - gvAddOnAmount);
//NJ-2011/10/25 Added gvAddOnAmount
break;
case "CARD":
if (PaymentTypeSupports(gvPaymentType, "multiple")
&& PaymentTypeSupports(gvPaymentType, "remember")
&& typeof(dlgpayment) == "dlgPaymentCreditCard") {
//.....
} else {
var dlgpayment = new dlgPaymentCreditCard();
}
break;
}
If the format() function is the standard VB6 one, you can either use the string.Format() function, or simply the .ToString() function.
You can access a variable inside a Window by callling WindowName.VariableName like this:
switch(paymentType)
{
case "CASH":
Window dlgPayment = new Window();
//Access variable lblChange in dlgPayment
dlgPayment.lblChange="something";
break;
case "CARD":
break;
default:
break;
}
Given that you create a class which inherited the Window class (Which represent the form) :
in xaml.cs
public partial class PaymentDialog : Window //inherit Window
{
public string lblChange {get;set;} //this should be a textblock/label in xaml, but i just assume it is a string.
}
You need to create instance of PaymentDialog by:
PaymentDialog dlgPayment = new PaymentDialog(); //notice the different
dlgPayment.lblChange = "XXX"; //you now can access it
Window dlgPaymentWindow = new Window();
dlgPaymentWindow.lblChange = "XXX";//you cannot access it since
//lblChange is not exist in WPF Window class
Related
I'm just starting to learn programming with Xamarin and C# and I really struggle with this question.
I have a master detail app. In my master page I would like to easily add buttons that link to a detail page.
To do that I would like to make a method that I can call to with two parameters:
Button text and the detail page linked to the button.
so basicly:
public class MainLink : Button
{
public MainLink(string name,string page)
{
Text = name;
Command = new Command(o => {
App.MasterDetailPage.Detail = new NavigationPage(new page);
App.MasterDetailPage.IsPresented = false;
});
}
}
This is what I have so far. But the line:
App.MasterDetailPage.Detail = new NavigationPage(new page);
ofcourse doesn't work.
How can I convert a string to a page type when I know the page's name already?
You can also use the awsome Activator.CreateInstance() which takes in a Type which would be your page.
In order to get from a string to a Type you could do one of two things. Either try to get the type using the string alone (which I have not actually tried in Xamarin Forms) like so:
Type somethingPageType = Type.GetType("SomethingPage");
Or you could just pass the page Type into your Button's constructor and use that like so:
....
public MainLink(string name,string page, Type pageType) {
....
}
So to put it all together, either:
public class MainLink : Button
{
public MainLink(string name,string page)
{
Text = name;
Command = new Command(o => {
Type pageType = Type.GetType(page);
App.MasterDetailPage.Detail = new NavigationPage(Activator.CreateInstance(pageType) as Page);
App.MasterDetailPage.IsPresented = false;
});
}
}
Or:
public class MainLink : Button
{
public MainLink(string name, Type pageType)
{
Text = name;
Command = new Command(o => {
App.MasterDetailPage.Detail = new NavigationPage(Activator.CreateInstance(pageType) as Page);
App.MasterDetailPage.IsPresented = false;
});
}
}
Two things to keep in mind though. First would be that if your page takes parameters into the page's constructor, you will need to use an overload of Activator.CreateInstance() to make that work.
Also, if you use the Xamarin Linker to remove unused stuff, it will probably mess this method up so you need to specifically tell the linker to leave your reflection code alone, for iOS and for Android
We do not link all code, only SDK Assemblies so we do not need to worry about the linker issue but it looks like you can use the following code on iOS to fix it (though I have not actually tested the code below):
Create a .cs file in your iOS project named whatever (LinkerIncludes.cs for example) and add the following code:
public class LinkerIncludes {
public void Include() {
var x = new System.ComponentModel.ReferenceConverter (typeof(void));
}
}
NavigationPage gets an instance of a page.
You can use a switch statement to initialize a new page, according to the page string:
switch (page)
{
case "page1":
App.MasterDetailPage.Detail = new NavigationPage(new Page1);
break;
case "page2":
App.MasterDetailPage.Detail = new NavigationPage(new Page2);
break;
default:
App.MasterDetailPage.Detail = new NavigationPage(new DefaultPage);
break;
}
I am trying to make some code more concise and am throwing an exception when I try to, I suppose, dynamically define a control. Whichever way I try, a and b always return as disposed. I could separate the cases out, but that would make the code twice as long. Any suggestion/comments would be much appreciated.
foreach (string[] str in items)
{
Control box = new Control();
CustomControlTypeA a = CustomControlTypeA();
CustomControlTypeB b = new CustomControlTypeB();
switch (str[4])
{
case "0":
a.richTextBox1.Rtf = str[5];
box = a;
break;
case "1":
b.panel1.BackgroundImage = Image.FromFile(str[5]);
box = b;
break;
}
a.Dispose();
b.Dispose();
box.Location = new Point(x,y);
box.Width = w;
box.Height = h;
panelBody.Controls.Add(box);
box.BringToFront();
}
I also tried defining CustomControlTypeA inside the case statement, redefining box as CustomControlTypeA, and even tried casting like so:
case "0":
(CustomControlTypeA)box.richTextBox1.Rtf = str[5];
break;
It's because you have a.Dispose(); & b.Dispose();. You're assinging one of them to box so it is also disposed.
Oddly though, you're creating box as a new Control (which you don't need to do), but not disposing that.
If you're adding a control to the .Controls collection you shouldn't be disposing it at all.
This code should work though:
foreach (string[] str in items)
{
Control box = null;
switch (str[4])
{
case "0":
CustomControlTypeA a = CustomControlTypeA();
a.richTextBox1.Rtf = str[5];
box = a;
break;
case "1":
CustomControlTypeB b = new CustomControlTypeB();
b.panel1.BackgroundImage = Image.FromFile(str[5]);
box = b;
break;
}
box.Location = new Point(x, y);
box.Width = w;
box.Height = h;
panelBody.Controls.Add(box);
box.BringToFront();
}
After lots of trial and error, I've now discovered that an aspect problem probably lies in the Rtf in my custom control, though who knows why that is. Enigmativity's method works if richtextbox.Text is used instead of richtextbox.Rtf.
EDIT: amended text to credit Enigmativity with solution. Either a typo elswhere in my code (setting str[5] to empty) or some other random code I changed in the meantime was exacerbating the problem.
I have my main form and a dialogbox which is called from main. In my main form I have a label and a button that which properties I can't change. I'm using Visual Studio 2015, not sure if there is a bug regarding this. I also made sure my label and button are set to public to modify.
Code: (this is from the dialog box, this has a list box the function is triggered at selectindexchange)
else if ((short)lbDiscountTypes.SelectedValue == 2) //Senior
{
frm_Main main = new frm_Main();
main.VAT = false;
main.labelStatus.Text = "NON-VAT (SENIOR)";
main.labelStatus.BackColor = System.Drawing.Color.IndianRed;
main.labelStatus.ForeColor = System.Drawing.Color.WhiteSmoke;
main.btnNonVat.Enabled = false;
main.btnNonVat.BackColor = System.Drawing.Color.SlateGray;
main.btnNonVat.ForeColor = System.Drawing.Color.Navy;
main.labelVatAmount.Text = 0.00m.ToString();
main.Dispose();
//INQUIRE DISCOUNT TYPES
var Discount = GC.CSHR_DiscountTypes.Where(Filter => Filter.DiscountCode == (short)lbDiscountTypes.SelectedValue);
decimal DP = 0.00m;
foreach (var item in Discount)
{
DP = item.DiscountPercentage;
}
foreach (var item in GC.CSHR_SORepo
.Where(Filter => Filter.Machine == MACHINE
&& Filter.SalesOrderNum == SALESORDERNUM
&& Filter.First_SRP == Filter.IMFSRP))
{
item.DiscountAmount = (item.SoldSRP * DP) / 100;
item.TotalAmount = (item.Quantity * item.SoldSRP) - item.DiscountAmount;
item.VATableSalesOnTotalAmount = (item.Quantity * item.SoldSRP) - item.DiscountAmount;
item.VATRate = 0.00m;
GC.SaveChanges();
}
Close();
}
The code below //INQUIRE DISCOUNT TYPES works well but not the one on top.
I've used debug mode to check if the lines are not being skipped over and they aren't.
You should pay attention to:
You are creating a new instance of your main form that you don't need (while it is open behind the dialog), so you need to get it not create a new instance
You are disposing the main form you created. main.Dispose();
In fact you are creating a new instance of main form and assigning values to those controls and then dispose it. While and instance of yor main form that you expect to see changes on it, is open and untouched behind your dialog.
To set value of those controls you can do one of these ways:
Option 1
Make your labelStatus and btnNonVat public. Open your main form in designer and select labelStatus and btnNonVat and in property grid, set Modifier to public. Then write this code:
//var main = Application.OpenForms.OfType<frm_Main>().FirstOrDefault();
var main = (frm_Main)Application.OpenForms["frm_Main"];
main.labelStatus.Text = "NON-VAT (SENIOR)";
main.labelStatus.BackColor = System.Drawing.Color.IndianRed;
main.labelStatus.ForeColor = System.Drawing.Color.WhiteSmoke;
main.btnNonVat.Enabled = false;
main.btnNonVat.BackColor = System.Drawing.Color.SlateGray;
main.btnNonVat.ForeColor = System.Drawing.Color.Navy;
main.labelVatAmount.Text = 0.00m.ToString();
Option 2
Pass an instance of your frm_Main to your dialog and work with it.
Option 3
After closing the dialog, use values from dialog and set values of your main form
Looks like you are trying to create new form using frm_Main main = new frm_Main(); syntax. All you need to do is get the instance of your current form.
var _currentMainForm= Application.OpenForms[0];
or if you have given name to your form
var _currentMainForm = Application.OpenForms["MainFormName"];
Once you get the reference you can perform all your label updates.
The code on top creates a new form, changes the labels and then disposes the form.
I think you should change the labels of the existing form.
Like in the other answer said you are setting properties of controls into a new Form object and not in the form where you come from.
You should pass the form object into the parameters of the dialog, something like:
void myDialog(frm_Main callingForm)
{
callingForm.Textbox1.Text = "abc";
}
And call it from you main form like this
...
myDialog(this);
The usage of question is a bit odd.
I am trying to return one of the TextBox, Button or even Label object from a function when I give string name of the object, like code below:
public [I don't know the type] getObjectClass(string type) {
switch(type) {
case "textbox":
return new TextBox();
break;
case "label":
return new Label();
break;
}
and finally I can access the object:
var obj = getObjectClass("label").Content = "I am new label!..";
You probably want dynamic for your return type.
public dynamic getObjectClass(string type)
Using Control, the lowest common ancestor to both Label and TextBox, would not let you access properties of the specific class that you create.
Note that this approach has risks, because the code is no longer type-safe. Another approach would be to use generics, but then you would have to specify the type along with a matching string with the type name, defeating the purpose of getObjectClass method.
Short answer: Find some class that is a superclass of all possible return types. For example, consider System.Windows.Controls.Control.
Additional info: You're trying to create a dynamic way of creating objects. This could be fine, for example for allowing your user to create their own interfaces, but I feel it is a potential code smell. For example, if you want to create a textbox and then set its text, you might do something like this:
Control myTextbox = CreateObjectFromTypename("textbox");
((TextBox)myTextbox).Text = "Hello, world!";
But now that you know the control will be cast into a textbox, why do you need to create it dynamically? Why couldn't you have used TextBox myTextbox = new TextBox()?
Again, I can't say for sure that your approach is bad, but I advice that you take care, and ensure that you really do need this dynamic creation.
I am guessing you want to add controls to the UI dynamically.
Since you have to set a property in the Control i.e. for TextBox you have to set the Text property, for Label you have you set the Content property. I suggest the following approach.
In the below sample I add a textbox and label to the UI dynamically.
The important piece in the below code is the Dictionary<Type, Action<Control, String>> property. In this Dictionary I define how to set the content for each Control depending on its Type.
Note:
I would suggest you to design in such a way that you don't separate the instance creation and property assignment into two different method. Do it in one single go. Check the new method with signature getObjectClass(string type, String content, Thickness margin).
XAML:
<Window x:Class="SplashScreenDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="800">
<StackPanel Name="StackPanelObj">
</StackPanel>
</Window>
Codebehind:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace SplashScreenDemo
{
public partial class MainWindow : Window
{
Dictionary<Type, Action<Control, String>> SetContent = new Dictionary<Type, Action<Control, String>>
{
{ typeof(TextBox), (control, content) => (control as TextBox).Text = content},
{ typeof(Label), (control, content) => (control as Label).Content = content}
};
public MainWindow()
{
InitializeComponent();
Control control = getObjectClass("textbox");
SetContent[control.GetType()](control, "This is a textbox");
Control control2 = getObjectClass("label");
SetContent[control2.GetType()](control2, "This is a label");
StackPanelObj.Children.Add(control);
StackPanelObj.Children.Add(control2);
}
public Control getObjectClass(string type)
{
switch (type)
{
case "textbox":
return new TextBox();
case "label":
return new Label();
default:
return null;
}
}
public Control getObjectClass(string type, String content, Thickness margin)
{
switch (type)
{
case "textbox":
var textBox = new TextBox();
textBox.Text = content;
textBox.Margin = margin;
return textBox;
case "label":
return new Label();
default:
return null;
}
}
}
}
First you need have full name of the type. Since you want to create WPF controls, concat control type name with 'System.Windows.Controls'. Then you need to use Type.GetType to create an instance of underlying type, and finally use Activator.CreateInstance to create an instance of the Control.
public Control CreateControlByTypeName(string typeName) {
string fullName = string.Format("System.Windows.Controls.{0}", typeName);
Type type = Type.GetType(fullName);
return (Control)Activator.CreateInstance(type);
}
Using dynamic keyword does the job
dynamic T;
switch (xnode.Type)
{
case "text":
T = new TextBox();
T.Text = "Enter your name";
Grid.SetColumn(T, 1);
Grid.SetRow(T, row);
break;
}
But I am not sure logically and overhead issues.
So based on your current function:
public [I don't know the type] getObjectClass(string type) {
The type will have to be something more generic like Control or Object, because you don't know the specific return type. If you are going to return multiple different types likes (Label, and TextBox), you have to return a parent class which they both inherit from.
This gets a little trickier
var obj = getObjectClass("label").Content = "I am new label!..";
as your base class (the return type) needs to have the Content property associated with it to be able access the property without casting. The easiest way around (although not my favorite) is to cast it ((Label) getObjectClass("label")).Content. This doesn't give you any compile time assurances though that the object you are returning actually has this property.
Personally I would write the method something like
public T getObjectClass<T> () where T: new() where T : Control {
return new T();
}
This way you can specify what you want to return and the generic automatically makes it the correct type. You also don't run into the problem of doing something like "lable" when you pass it in as a string and have it fail at runtime.
I am using student record entry window form. I want that after submitting data, all fields of form(i.e radio button, combobox etc) and messages(warning and successful) should be reset so that new user can add data.
is their any built in function to reset form in csharp?
or I have to write clear method for this?
or can I regenerate the form?
You must first clear the controls and then use InitializeComponent method to work perfectly.
this.Controls.Clear();
this.InitializeComponent();
This you can achieve in two ways:-
1) you can clear the fields of the form in one method. say public void clear() And whenever you want to clear the form simply call this method.
2) In second way you can destory the instance of the form and generate the new instance of the same form and then show this.
I will recomeded 1st one for you.
This is what I used to create in my every page
private void ClearControls()
{
try
{
txtUserName.Text = string.Empty;
//txtPassword.Text = string.Empty;
txtFName.Text = string.Empty;
txtMName.Text = string.Empty;
txtLName.Text = string.Empty;
lblUserType.Text = string.Empty;
btnSave.Text = "Save";
fnMessage(false, "");
}
catch (Exception ex)
{
fnMessage(true, ex.Message);
}
}
Thanks
Implement data binding to a given object (good starting point here)
For resetting the form, create a new object and the binding will do it for you.
HTH
You can either re-create the form instance, or perhaps try something similar to this (untested):
foreach (Control ctl in this.Controls)
{
switch (ctl.GetType().ToString())
{
case "TextBox":
ctl.Text = null;
break;
case "ComboBox":
ctl.Text = null;
break;
}
}
Clearly, you can include as many different types of control as you wish and introduce other critieria (i.e. where control name begins with 'xyz' or where control resides within a particular panel).
Compared to other suggestions, the advantage of this approach is that if you have dozens of the same control type (typically textboxes), a few lines of code cover the lot. Additionally, if you add more controls of the covered types, you don't need to revisit the code to update it. Perhaps you could even create it as an extension method of your forms?
If form creation doesn't take too much resources it is easier to create new instance.
As i had same problem and my from was having nested controls. I tried CJM's method and it worked however i had to write a recursive function because of controls nesting (tab controls, containers, user controls etc)
Try out the following snippet if you want to clear whole form recursively
private void clearRecursive(Control control)
{
foreach (Control subcontrol in control.Controls)
{
switch (subcontrol.GetType().ToString().Replace("System.Windows.Forms.", ""))
{
case "TextBox":
TextBox textBox = (TextBox)subcontrol;
textBox.Text = null;
break;
case "ComboBox":
ComboBox comboBox = (ComboBox)subcontrol;
if (comboBox.Items.Count > 0)
comboBox.SelectedIndex = 0;
break;
case "CheckBox":
CheckBox checkBox = (CheckBox)subcontrol;
checkBox.Checked = false;
break;
case "RadioButton":
RadioButton radioButton = (RadioButton)subcontrol;
radioButton.Checked = false;
break;
case "TreeView":
TreeView tv = (TreeView)subcontrol;
foreach (TreeNode node in tv.Nodes)
{
node.Checked = false;
CheckChildren(node, false);
}
break;
case "ListBox":
ListBox listBox = (ListBox)subcontrol;
listBox.Items.Clear();
break;
case "CheckedListBox":
CheckedListBox chklstbox = (CheckedListBox)subcontrol;
for (int i = 0; i < chklstbox.Items.Count; i++)
{
chklstbox.SetItemCheckState(i, CheckState.Unchecked);
}
break;
}
if (subcontrol.HasChildren)
clearRecursive(subcontrol);
}
}
Call InitializeComponent method and Form_Load method.