Para o .NET 2.0, aqui está um bom código que escrevi que faz exatamente o que você deseja e funciona para qualquer propriedade em Control
:
private delegate void SetControlPropertyThreadSafeDelegate(
Control control,
string propertyName,
object propertyValue);
public static void SetControlPropertyThreadSafe(
Control control,
string propertyName,
object propertyValue)
{
if (control.InvokeRequired)
{
control.Invoke(new SetControlPropertyThreadSafeDelegate
(SetControlPropertyThreadSafe),
new object[] { control, propertyName, propertyValue });
}
else
{
control.GetType().InvokeMember(
propertyName,
BindingFlags.SetProperty,
null,
control,
new object[] { propertyValue });
}
}
Chame assim:
// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);
Se você estiver usando o .NET 3.0 ou superior, poderá reescrever o método acima como um método de extensão da Control
classe, o que simplificaria a chamada para:
myLabel.SetPropertyThreadSafe("Text", status);
ATUALIZAÇÃO 10/05/2010:
Para o .NET 3.0, você deve usar este código:
private delegate void SetPropertyThreadSafeDelegate<TResult>(
Control @this,
Expression<Func<TResult>> property,
TResult value);
public static void SetPropertyThreadSafe<TResult>(
this Control @this,
Expression<Func<TResult>> property,
TResult value)
{
var propertyInfo = (property.Body as MemberExpression).Member
as PropertyInfo;
if (propertyInfo == null ||
!@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
@this.GetType().GetProperty(
propertyInfo.Name,
propertyInfo.PropertyType) == null)
{
throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
}
if (@this.InvokeRequired)
{
@this.Invoke(new SetPropertyThreadSafeDelegate<TResult>
(SetPropertyThreadSafe),
new object[] { @this, property, value });
}
else
{
@this.GetType().InvokeMember(
propertyInfo.Name,
BindingFlags.SetProperty,
null,
@this,
new object[] { value });
}
}
que usa expressões LINQ e lambda para permitir uma sintaxe muito mais limpa, mais simples e segura:
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile
Além de o nome da propriedade agora ser verificado no momento da compilação, o tipo da propriedade também é, portanto, é impossível (por exemplo) atribuir um valor de string a uma propriedade booleana e, portanto, causar uma exceção de tempo de execução.
Infelizmente, isso não impede ninguém de fazer coisas estúpidas, como passar Control
a propriedade e o valor de outra pessoa , de modo que o seguinte será felizmente compilado:
myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);
Portanto, adicionei as verificações de tempo de execução para garantir que a propriedade transferida realmente pertença à Control
qual o método está sendo chamado. Não é perfeito, mas ainda é muito melhor que a versão .NET 2.0.
Se alguém tiver mais sugestões sobre como melhorar esse código para segurança em tempo de compilação, comente!