This is just a matter of taste but I'd like to hear some of your opinions (that's also why this question is marked as subjective).
If I have a property, say
private string _Text;
public string Text;
get
{
object tmp = ViewState["Text"];
if (tmp != null)
_Text = Convert.ToString(tmp);
return _Text;
}
set
{
ViewState.Add("Text", value);
}
Now this is the property which may be specified by the programmer, by setting some custom text. This is then mapped - say - to some control on the UI. In the default case however, the Text of the control comes from a predefined resource file. So internally to handle that internally in a better way, I'd have some central point where I check whether the user has specified the "Text" property (above) and if so, use that data, otherwise rely on the default one from the resource file.
So what approach would you take? I have two options in mind:
private string ResolvedText
{
get
{
if(!string.IsNullOrEmpty(Text))
return Text;
else
//return the one from the resource file
}
}
Or put everything in a method
public string GetResolvedText()
{
if(!string.IsNullOrEmpty(Text))
return Text;
else
//return the one from the resource file
}
The question may sound stupid to you since it's really a minor difference. But I'd like to know whether there are some conventions about this.
Thx
Personally, I'd take the body of your GetResolvedText method, and use it in the property, thus:
private string _Text;
public string Text
{
get
{
if(string.IsNullOrEmpty(_Text))
//return the one from the resource file
else
return _Text;
}
set
{
_Text = value;
}
}
This puts all the responsibility for managing the string into the one place. The class itself can access _Text internally, if it needs the raw value.
I find that the best general rule here is: if calling the action twice results in multiple resource calls or different behaviour - use a method.
So, in your example use of a property is fine if it caches:
public string ResolvedText
{
get { return Text ?? (Text = GetResolvedText()); }
}
However the method doesn't need to - users expect it to be a more intensive operation:
public string GetResolvedText()
{
//return the one from the resource file
}
The design question is how do you want this class to be used?
A property will get called as if it is a 'cheap' operation:
if( myInstance.ResolvedText != null &&
myInstance.ResolvedText.Length > 5 )
Response.Write( myInstance.ResolvedText );
A method hints to the developer that they should call it as few times as possible:
string resolvedText = myInstance.GetResolvedText();
if( resolvedText != null &&
resolvedText.Length > 5 )
Response.Write( resolvedText );
Personally I prefer to keep interim classes simple, so in the vast majority of cases I would use the method model.
As this is a fairly standard convention you should avoid properties that don't cache and methods that do.
I would keep this as a property, since it represents a single value that does not require a lot of computing to retrieve.
To me, if the getter can't throw an exception, or null/invalid value, it should be a property. It is what properties are made for.
BUT, if you do some complicated stuff, if has to be a a function getter. Obviously here you have only 1 if, so I would use a property.
Reworking together your example and Steve's answer, plus adding in some caching, as obviously the resource value should be read only once since it never changes and by contract we must return the value from the property as fast as possible:
private static string ResourceText;
static [constructor]
{
ResourceText = //get resource;
}
private string text;
public string Text;
get
{
string tmp = (string)ViewState["Text"];
if (!String.IsNullOrEmpty(tmp))
text = tmp;
else
text = ResourceText;
return text;
}
set
{
ViewState.Add("Text", value);
// Note: passing null or empty strings will not work.
}
Related
I have this project:
And I need to use a variable that is in the "TransactionHandler.cs" in the "Enviar Faturas.cs" the TransactioHandler is a class library and the Enviar Faturas is a Windows Form.
Is it possible to do what I want? If so how should I do it?
UPDATE:
I have this variable declares in TransactionHandler.cs
var numfatura = _transaction.TransDocument + _transaction.TransSerial + _transaction.TransDocNumber;
And I need to use it on the Windows Form "Enviar Faturas".
UPDATE 2:
I have this code to select from a datagridview and write a textfile:
FileInfo arquivo = new FileInfo(#"C:\Users\HP8200\Desktop\faturas\" + r.Index + ".txt");
And I want to change the "r.index" for the variable I showed on the first update
I would suggest to use a property instead of a public field:
public class TransactionHandler
{
private static string numfatura = _transaction.TransDocument + _transaction.TransSerial + _transaction.TransDocNumber;
public static string Numfatura
{
get { return numfatura ; }
set { numfatura = value; }
}
}
From another class, you call your variable like this:
public class EnviarFaturas
{
public void DoSomething()
{
string r.Index= TransactionHandler.Numfatura;
}
}
Ok, from what I understand and having no idea of the execution flow you probably need something like this in the TransactionHandler (a property)
public int Numfatura
{
get
{
return this._transaction.TransDocument + this._transaction.TransSerial + this._transaction.TransDocNumber;
}
}
you can change the type to the one that stands behing the "var" in your code example.
To access it in the form you need an instance of the class (again I dont know what your logic is) but once you get it e.g.
var transactionHandler = new TransactionHandler();
you can simply try
r.Index = transactionHandler.Numfactura;
Keep in mind that you can hit the default data value (for int is 0) if your methods depend upon other event to happen.
I strongly suggest you to learn more about C# and Object Oriented Programming as Alexey Zimarev stated in the comments.
Also you should consider how to get/inject a concrete instance in the view.
Another good and related read will be singleton pattern, mvp and dependency injection.
What I want to do is have the user add items to the list. Then when they add an item I need the list to save, so that when the user closes the app and opens it again, the list they've created is still there.
Right now, I can add items to my list, but as soon as i close the app they will be gone.
private static ObservableCollection<ViewModels.ZoneViewModel> Zones = new ObservableCollection<ViewModels.ZoneViewModel>();
public void PopulateListView(string image, string name, string address)
{
if (name != "" && address != "")
{
Zones.Add(new ViewModels.ZoneViewModel { Image = image, Name = name, Address = address });
Application.Current.Properties["zoneslist"] = Zones;
}
}
protected override void OnAppearing()
{
if (Application.Current.Properties.ContainsKey("zoneslist"))
{
// Put the contents of the "zoneslist" key into a variable as a string.
var savedZones = Application.Current.Properties["zoneslist"] as ObservableCollection<ViewModels.ZoneViewModel>;
// Set the listviews' itemssource to the savedzones list.
zonesList.ItemsSource = savedZones;
}
}
Here's the code I use right now, I thought this could work to save it but that doesn't work.
EDIT: So I've tried what #Alessandro Calario suggested and after using json serialization the listview just gives me a ton of empty list items(even though i only added one). But an item is added and is saved, even when the app is closed. Progress, at least, but I'm not quite there yet. Anyone know a solution?
my code:
public void PopulateListView(string image, string name, string address)
{
if (name != "" && address != "")
{
Zones.Add(new ViewModels.ZoneViewModel { Image = image, Name = name, Address = address });
//Serialize to json string
var json = JsonConvert.SerializeObject(Zones);
Application.Current.Properties["zoneslist"] = json;
}
}
protected override void OnAppearing()
{
if (Application.Current.Properties.ContainsKey("zoneslist"))
{
// Put the contents of the "zoneslist" key into a variable as a string.
var savedZones = Application.Current.Properties["zoneslist"] as string; //ObservableCollection<ViewModels.ZoneViewModel>
JsonConvert.DeserializeObject<ObservableCollection<ViewModels.ZoneViewModel>>(savedZones);
// Set the listviews' itemssource to the savedzones list.
zonesList.ItemsSource = savedZones;
}
}
I think you can Serialize your List of Objects to a json String and save it to Application Properties
If using 3rd parties libraries is not a thing for your project I highly recommend you to use Akavache. This is an Async, persistent key-value store.
Once setup is very simple to use.
//To Insert your object
IObservable<Unit> InsertObject<T>(string key, T value, DateTimeOffset? absoluteExpiration = null);
//To Get your object
IObservable<T> GetObject<T>(string key);
where T can be your whole list.
Of course it's a little more than this but trust me just a little. Read the full documentation and hope it fits your needs.
The Application Properties only stores primitive types.
Note: the Properties dictionary can only serialize primitive types for
storage. Attempting to store other types (such as List can
fail silently).
Source: https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/application-class/
Either set it up so you are using the properties as a primitive storage, or go for another local storage mechanism such as Sqlite (a good guide here: https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/databases/)
All, I am localising my C# application and I have come to my settings control. As I am using a PropertyGrid I am using properties to provide the options. I have a CategoryAttribute for "Server Access" I need to show in different languages - I had:
[CategoryAttribute("ServerAccess"),
ReadOnlyAttribute(false),
XmlElement,
DescriptionAttribute("Message about the Server Access Option.")]
public bool ShowSystemDatabases
{
get { return this.showSystemDatabases; }
set { this.showSystemDatabases = value; }
}
and I attempted to change this to:
[CategoryAttribute(ObjectStrings.SettingsServerAccess),
ReadOnlyAttribute(false),
XmlElement,
DescriptionAttribute(MessageStrings.SettingsShowSystemDatabaseInfo)]
public bool ShowSystemDatabases
{
get { return this.showSystemDatabases; }
set { this.showSystemDatabases = value; }
}
Where ObjectStrings is my resource file ('ObjectStrings.resx' et al.). This is throwing a compile-time error
An attribute argument must be a constant expression, typeof expression or array
creation expression of an attribute parameter type...
clearly I can't use a string when the constructor is expecting a const string. I have tried casting from string to const string using various round-the-houses method but all have failed. it is a simple enough issue, but...
What is the easiest way around this problem?
Thanks for your time.
I don't know how you could handle this especially in the case of control's attributes, but a general way to achieve this is to make Resources resolved at runtime, using parameters like these on your attribute :
MessageResourceType = typeof (MyResource), MessageResourceName = "MyResourceKey")
instead of directy passing the pointer to resource key.
I don't know if there is an equivalent way for CategoryAttribute, DescriptionAttribute and others Controls' properties attributes, or if there is a way to overload them.
I am a bit new at C# and I have run into a string concatenation issue. I am hoping someone might be able to give me a hint and help me resolve this. I have searched Google extensively and have spent more than a week on this so any help/advice would be greatly appreciated.
I have created a custom PathEditor for a string property. The property basically allows the user to key in a file to use in the app. If the file typed in is correct, it shows in the property cell as it should. What I am trying to do is output to the property cell an error message if the file typed in does not exist - I check this in my file validator. Here is the string literal issue.
If I use:
return inputFile+"Error_";
this works OK and I get the outpur file123.txtError_ in the property grid cell.
If I use:
return "Error_"+inputFile;
I get only the inputFile without the literal "Error_". Sot he property grid cell shows file123.txt in the property grid cell.
I have checked and inputFile is a string type. Any ideas as to why this is happening?
Also, is there any way to change to font, and/or, color of the message output? I tried to change the background of the property grid cell and I understand that this is not possible to do.
Thank you.
Z
More of the code:
[
Description("Enter or select the wave file. If no extension, or a non .wav extension, is specified, the default extension .wav will be added to the filename."),
GridCategory("Sound"),
Gui.Design.DisplayName ("Input Sound"),
PathEditor.OfdParamsAttribute("Wave files (*.wav)|*.wav", "Select Audio File"),
Editor(typeof(PathEditor), typeof(System.Drawing.Design.UITypeEditor))
]
public string InputWavefile
{
get { return System.IO.Path.GetFileName(inputtWavefile); }
set
{
if (value != inputWavefile) // inputWavefile has been changed
{
// validate the input stringg
_inputWavefile = FileValidation.ValidateFile(value);
// assign validated value
inputWavefile = _inputWavefile;
}
}
}
My guess is that you've got a funky character at the start of inputFile which is confusing things - try looking at it in the debugger using inputFile.ToCharArray() to get an array of characters.
The string concatenation itself should be fine - it's how the value is being interpreted which is the problem, I suspect...
I'm guessing your filename looks something like this, C:\Folder\FileName.txt when you start out.
In your FileValidation.ValidateFile() method you
return "Error_" + InputFileName;
it now looks like this: Error_C:\Folder\FileName.txt.
So, when you run the line below,
get { return System.IO.Path.GetFileName( _inputWavefile ); }
it strips off the path and returns the filename only, FileName.txt.
Even when the filename is not valid, you are still running System.IO.Path.GetFileName() on it.
Assuming this is a PropertyGrid in winforms app. Then it's neither a string concatenation issue, nor PropertyGrid issue, as could be proven by the following snippet. So you need to look elsewhere in your code:
public partial class Form1 : Form {
PropertyGrid pg;
public Form1() {
pg = new PropertyGrid();
pg.Dock = DockStyle.Fill;
this.Controls.Add(pg);
var inputFile = "some fileName.txt";
var obj = new Obj();
obj.One = "Error_" + inputFile;
obj.Two = inputFile + "Error_";
pg.SelectedObject = obj;
}
}
class Obj {
public string One { get; set; }
public string Two { get; set; }
}
I have a text box, which users are allowed to enter addresses in these forms:
somefile.htm
someFolder/somefile.htm
c:\somepath\somemorepath\somefile.htm
http://someaddress
\\somecomputer\somepath\somefile.htm
or any other source that navigates to some content, containing some markup.
Should I also put a drop down list near the text box, asking what type of address is this, or is there a reliable way that can auto-detect the type of the address in the text box?
I don't think there is a particularly nice way of automatically doing this without crafting your own detection.
If you don't mind catching an exception in the failure case (which generally I do), then the snippet below will work for your examples (noting that it will also identify directories as being of type file)
public string DetectScheme(string address)
{
Uri result;
if (Uri.TryCreate(address, UriKind.Absolute, out result))
{
// You can only get Scheme property on an absolute Uri
return result.Scheme;
}
try
{
new FileInfo(address);
return "file";
}
catch
{
throw new ArgumentException("Unknown scheme supplied", "address");
}
}
I would suggest using a regex to determine the paths, similar to
public enum FileType
{
Url,
Unc,
Drive,
Other,
}
public static FileType DetermineType(string file)
{
System.Text.RegularExpressions.MatchCollection matches = System.Text.RegularExpressions.Regex.Matches(file, "^(?<unc>\\\\)|(?<drive>[a-zA-Z]:\\.*)|(?<url>http://).*$", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
if (matches.Count > 0)
{
if (matches[0].Groups["unc"].Value == string.Empty) return FileType.Unc;
if (matches[0].Groups["drive"].Value == string.Empty) return FileType.Drive;
if (matches[0].Groups["url"].Value == string.Empty) return FileType.Url;
}
return FileType.Other;
}
If there is only a limited number of formats, you can validate against these and only allow valid ones. This will make auto-detection a bit easier as you will be able to use the same logic for that.
Check Uri.HostNameType Property and Uri.Scheme Property