Eftersom vi tydligen har i alla fall 4 stycken extremt duktiga utvecklare i den här tråden så kan ni ju svara på den här frågan gällande att hantera händelser i en klass. Jag har fält som heter width, length och thickness. Dessa ska få ett värde tilldelat sig när Widths, Lengths och Thicknesses får ett värde. Hur ska jag hantera det korrekt? Det är fält som inte ska inte sparas i någon databas utan bara användas för att räkna ut volym och lite annat. Sedan behöver jag ju en metod för att räkna ut värdena. Jag har nu suttit och snorat över det här en stund och får inte riktigt till någon lösning som jag känner blir tillräckligt "snygg" designmässigt. Kollegan vill att jag räknar ut allting i Form1.cs vilket jag givetvis vägrar.
Jag är rätt basic på det här ämnet och nu ska allting vara jäkligt privat. Jag vill tex inte i Form1.cs behöva sätta en
value.OnValueChanged +=
eftersom det ska skötas helt backend. Får inte ihop det på något smidigt sätt.
Du skulle nog kunna göra så att du i alla dina set när du satt nya värdet anropar en metod som läser av klassesn tillstånd, räknar om värderna och sätter om dem.
Sen ha ett event på klassen för att säga till koden utanför ex formet att nu har värderna ändras läs om och visa den nya. Typ. Är det nått sånt du vill åt?
Då skulle jag skapar event och delegate på min entiet.
Sen skulle jag skapa en metod som är privat (om man inte vill räkna om den när som, då publik.) metoden ligger självklart i entiteten (klassen) den läser helt enkelt in propparna och räknar om nya värden som den sätter om. När denne metod körts triggar den bara eventmetoden som då form antagligen lyssnar på och vet att nu har nått hänt.
public partial class Packages
{
private int length { get; set; }
private int width { get; set; }
private int thickness { get; set; }
public string Lengths { get; set; }
public string Widths { get; set; }
public string Thicknesses { get; set; }
public decimal Volume { get; set; }
public decimal RunningMetre { get; set; }
public decimal Stuage { get; set; }
}
Widths, Lengths, Thicknesses är en "range" av tillåtna värden för det som finns i paketet. Att välja rätt där är ju inga problem men jag vill ha resten av logiken på samma ställe osynligt för den som använder klassen. Så jag behöver då några privata event i klassen för att sätta rätt värden osv. Length, Width och Thickness kommer aldrig användas annat än till beräkning och sparas heller ingenstans.
Hmmm, måste du ha det som en sträng? Kan du inte representera en range som ett Range objekt?
public string Widths
{
get { return this.Widths; }
set
{
this.Widths = value;
if (this.WidthsChanged != null)
{
this.WidthsChanged(value);
}
}
}
Ser ut som du anropar din autoproperty som finns i din klass med exakt samma namn. När man inte har nytta av autoproperty som du egentligen inte har eftersom du vill lägga in funktionalitet så tycker jag att man nog skall göra ett vanligt field
private string widths;
Hmm. annars hänger jag inte riktigt med exakt vad du vill göra kanske är lättare med lite mer beskrivning.
Ett paket innehåller olika längder, bredder och höjder. Det exakta innehållet i paketet sparas inte (fråga inte mig varför, när jag ställde den frågan vidare så undrade de andra det också.). För att på en höft få fram volym osv behöver vi hämta min-max värdena från sorterings tabellen. De ser ut som "18-54" och sparas som varchar.
Nu vill jag då på ett cleant sett ta det ena värdet 18 eller 54 och räkna ut volymen. Jag kan komma på ett flertal olika fullösningar och hacks men inget som känns riktigt bra. Jag har blivit helt frälst i att lägga för mycket tid på "snygg" kod tyvärr. Efter allt tjat om att göra rätt i andra trådar så tänkte jag att jag skulle slänga ihop något vackert istället för att fortsätta med fullösningar. ;)
Okej, men en Range klass tycker jag ändå att du skall ha, även om det sparas som en varchar.
Möjligtvis att det skall till lite events på denna också.
Samt att man kanske skall kunna sätta
NumberRange numberRange = "10-20"; och få allting satt...
public class NumberRange
{
public int Left { get; set; }
public int Right { get; set; }
public override string ToString()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(Left.ToString());
stringBuilder.Append(" - ");
stringBuilder.Append(Right.ToString());
return stringBuilder.ToString();
}
Ah nu vaknade jag till usch vad efterbliven man är när man är förkyld! Självklart har du rätt Nickemannen! Det var precis den snygga lösningen jag letade efter. Jag vågar inte berätta vilka andra saker jag INTE tänkt på idag hehe
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BL.RetailSystem.Application.Shared.Entity
{
public class MyClass
{
#region -- Declaration
private decimal _Width;
private decimal _Length;
private decimal _Depth;
public event SizeChangedEventHandler SizeChanged;
#endregion
#region -- Properties
public decimal Width
{
get { return _Width; }
set {
_Width = value;
OnSizeChanged();
}
}
public decimal Length
{
get { return _Length; }
set {
_Length = value;
OnSizeChanged();
}
}
public decimal Depth
{
get { return _Depth; }
set {
_Depth = value;
OnSizeChanged();
}
}
public decimal Volym
{
get { return CalculateVolym(); }
}
#endregion
#region -- Methods
private void OnSizeChanged()
{
if (SizeChanged != null)
SizeChanged();
}
private decimal CalculateVolym()
{
return this.Width * this.Length * this.Depth;
}
#endregion
}
}
Det kräver dock att du lyssnar på eventet SizeChanged från din entitet i din winform, så du kan ändra det värdet sedan. Antingen så som här att du frågar entiteten efter dess olika properties, eller så ändrar du i SizeChangedEventArgs och skickar med de olika värdena där. Vilket fall som helst så kräver det kod av dig att "binda om" dina värden.
Så därför har jag ett annat förslag till dig, och detta kommer kännas väldigt bakvänt till en början, men oj så smidigt det är. Och det är att använda databinding för att bind din properties på dina entiteter till olika fält i din winform. Så du binder helt enkelt propertyen volymn till ett textfält och när du sen ändrar på någon av din property width, length, depth. Så triggar du igång ett NotifyPropertyEvent för din volymn och vips så har värdet ändrats i ditt textfält.
Det är riktigt smidigt, även om det är flera gånger bättre i WPF än i Winform så fungerar det hyfsat bra i winform.
Ta och läs på information om Databindings och speciellt följande interface: INotifyPropertyChanged, IDataErrorInfo, IEditableObject. Jag har en basklass för mina entiteter som använder sig av detta som ser ut så här (johan blunda nu!!!)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Reflection;
using System.Collections;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace TS.Classes
{
[DataContract(Name = "baseentity", Namespace = "http://classes.gladh.org")]
[Serializable]
[XmlType(TypeName = "baseentity", Namespace = "http://classes.gladh.org/")]
public class BaseEntity : INotifyPropertyChanged, IDataErrorInfo, ICloneable, IEditableObject
{
#region -- declaration
[field: NonSerialized]
private event PropertyChangedEventHandler PropertyChanged;
//-- Holds information about the errors added
[field: NonSerialized]
private Dictionary<string, string> _ErrorDictonary = new Dictionary<string, string>();
[field: NonSerialized]
private bool _HasNoErrors = true;
//-- Holds information about all changes to proprerties in this object.
[field: NonSerialized]
private Hashtable _Propreties;
[field: NonSerialized]
private bool _IsEdit = false;
#endregion
#region -- Properties
[EditablePropertyNotSupported]
public bool IsEdit
{
get { return _IsEdit; }
set { _IsEdit = value; }
}
[EditablePropertyNotSupported]
private string Error
{
get
{
if (_ErrorDictonary.Count != 0)
return "An error has occured";
else
return string.Empty;
}
}
protected bool HasNoErrors
{
get { return _HasNoErrors; }
set { _HasNoErrors = value; }
}
protected string this[string columnName]
{
get
{
if (_ErrorDictonary == null)
_ErrorDictonary = new Dictionary<string, string>();
if (_ErrorDictonary.ContainsKey(columnName))
return _ErrorDictonary[columnName];
else
return string.Empty;
}
set
{
if (_ErrorDictonary == null)
_ErrorDictonary = new Dictionary<string, string>();
if (_ErrorDictonary.ContainsKey(columnName))
if (string.IsNullOrEmpty(value))
_ErrorDictonary.Remove(columnName);
else
_ErrorDictonary[columnName] = value;
else
_ErrorDictonary.Add(columnName, value);
}
}
#endregion
#region -- Methods
public void NotifyPropertyChanged(String info)
{
NotifyPropertyChanged(info, true);
}
public void NotifyPropertyChanged(String info, bool fieldEdit)
{
if(fieldEdit) this.IsEdit = true;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
/// <summary>
/// When using the Binding object in .NET we need a way to handle changes to our entites
/// </summary>
public virtual void BeginEdit()
{
this.IsEdit = false;
PropertyInfo[] properties = (this.GetType()).GetProperties(BindingFlags.Public | BindingFlags.Instance);
_Propreties = new Hashtable(properties.Length - 1);
for (int i = 0; i < properties.Length; i++)
{
object[] found = properties[i].GetCustomAttributes(typeof(EditablePropertyNotSupportedAttribute), true);
if (found != null && found.Length != 0)
continue;
if (null != properties[i].GetSetMethod())
{
Type cloneableType = properties[i].PropertyType.GetInterface("ICloneable", true);
object value = null;
if (cloneableType != null)
{
ICloneable cloneable = properties[i].GetValue(this, null) as ICloneable;
if (cloneable != null)
value = cloneable.Clone();
else
value = properties[i].GetValue(this, null);
}
else
value = properties[i].GetValue(this, null);
_Propreties.Add(properties[i].Name, value);
}
}
}
/// <summary>
/// When cancel an entity that has a Binding, we need to be able to set the old value back.
/// </summary>
public virtual void CancelEdit()
{
if (null == _Propreties)
return;
PropertyInfo[] properties = (this.GetType()).GetProperties(BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < properties.Length; i++)
{
if (null != properties[i].GetSetMethod() && _Propreties.ContainsKey(properties[i].Name))
{
object value = _Propreties[properties[i].Name];
properties[i].SetValue(this, value, null);
}
}
EndEdit();
}
/// <summary>
/// Clean up after us when an edit is completed.
/// </summary>
public virtual void EndEdit()
{
_Propreties = null;
this.IsEdit = false;
}
/// <summary>
/// Create a deep copy of this object
/// </summary>
/// <remarks>
/// <para>
/// We use the serialization to perfome the DeepCopy, that means that the object needs to have the Serialized attribute sat on it, or implement the ISerializeble interface
/// </para>
/// </remarks>
/// <returns></returns>
public virtual object Clone()
{
if (this is ISerializable)
throw new NotSupportedException("The object that shall be cloned need to implement the ISerializable interface");
BinaryFormatter binaryFormatter = new BinaryFormatter();
MemoryStream memoryStream = new MemoryStream();
binaryFormatter.Serialize(memoryStream, this);
memoryStream.Position = 0;
return binaryFormatter.Deserialize(memoryStream);
}
public virtual bool Validate() { return true; }
protected virtual void ClearError(string propertyName)
{
ClearError(propertyName, true);
}
/// <summary>
///
/// </summary>
/// <summary>
/// Clear any errormessage that may be set on the property
/// </summary>
/// <param name="propertyName"></param>
protected virtual void ClearError(string propertyName, bool isEdit)
{
this[propertyName] = string.Empty;
this.NotifyPropertyChanged(propertyName, isEdit);
}
/// <summary>
/// Defines error on diffrent properties for the entity
/// </summary>
/// <remarks>
/// <para>
/// this metod will raise an NotifyPropertyChanged() event for the propertyName, to let the UI know something has happend with this property
/// </para>
/// </remarks>
/// <param name="propertyName"></param>
/// <param name="errorMessage"></param>
protected virtual void SetError(string propertyName, string errorMessage)
{
this.HasNoErrors = false;
this[propertyName] = errorMessage;
this.NotifyPropertyChanged(propertyName, false);
}
#endregion
#region -- Constructor
public BaseEntity() { }
#endregion
#region -- INotifyPropertyChanged Implementation
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add { PropertyChanged += value; }
remove { PropertyChanged -= value; }
}
#endregion
#region -- IDataErrorInfo Members
string IDataErrorInfo.Error
{
get { return this.Error; }
}
string IDataErrorInfo.this[string columnName]
{
get
{
return this[columnName];
}
}
#endregion
#region -- ICloneable Members
object ICloneable.Clone()
{
return Clone();
}
#endregion
#region IEditableObject Members
void IEditableObject.BeginEdit()
{
BeginEdit();
}
void IEditableObject.CancelEdit()
{
CancelEdit();
}
void IEditableObject.EndEdit()
{
EndEdit();
}
#endregion
}
}
Så hem och lek lite på kvällen med databindings så kan du föreslå att ni skriver om hela koden efter helgen :)
Dessutom så måste du ju älska regioner om du har så många metoder som du "skryter om" :)..
Skulle få frispel på alla metoder om jag inte fick gömma dem med regioner....
- M
haha... nja så många blir det ju inte. :-)
Det blir nästan färre då det blir mindre kod. Har rätt små klasser oxå. Blir lite så automatiskt. Så svårt att förklara i text bara varför.
Men ang regioner. Varför? det är så störigt att öppna o stänga kod hela tiden, sen gömmer man ju massa viktig info... nä aldrig förstått mig på sånt. det är som att vika massa sidor åt olika håll i en bok så måste man vika upp dem för att få läsa boken. :birp
Dock kan det va ok när man har dmeokod. bara för att folk skall fokusera på den kod man vill dema och inte kod runtomkring...
En gammal kollega till mig skrev en helt ok blogpost om sin frustration ang regioner:
Men ang regioner. Varför? det är så störigt att öppna o stänga kod hela tiden, sen gömmer man ju massa viktig info... nä aldrig förstått mig på sånt. det är som att vika massa sidor åt olika håll i en bok så måste man vika upp dem för att få läsa boken. :birp
Tycker det är bra med regioner så länge som man inte missbrukar det. Brukar hålla mig till något i stil med:
Using Directives
Fields
Constructor(s)
Properties
Methods
Tycker det är skönt att kunna minimera det jag inte vill se när arbetar i en klass. Så länge man håller sig till ett lägre antal och tydligt definierade regioner så tycker jag det är okej.
Tycker det är bra med regioner så länge som man inte missbrukar det. Brukar hålla mig till något i stil med:
Using Directives
Fields
Constructor(s)
Properties
Methods
Tycker det är skönt att kunna minimera det jag inte vill se när arbetar i en klass. Så länge man håller sig till ett lägre antal och tydligt definierade regioner så tycker jag det är okej.
Men varför vill du gömma delar? du vill ju se vad din kod gör? Scrolla upp o ner brukar fungera ganska bra när man läser kod... Kod skall läsas som en uppsatts inte som en en dynamisk berättelse där man måste dra i ett dragspelspapper för att förstå vad som händer.
Regioner gömmer bara viktig information för dig och andra. Främst är det andra som kommer lida mest och du själv i framtiden när du går tillbaka till legazy kod och gärna vill snabbt förstå vad koden gör. då vill du inte sitta och expandera massa regioner, du vill snabbt se flödet i din kod.
Allra minst gör det ingen nytta att gömma proppar om du ändå måste trycka fram dem...
Säg inte nu men man kan expandera alla med en gång... Jepp men varför skall man då ha dem?
:bire
263 ms totalt · 4 externa anrop · v20260731065814-full.86ec41c2