在运行时给某个实例注入附件属性,主要有三个类:AttachedPropertiesService , AttachedProperty ,AttachedProperty<T>。
下面是测试程序:
using System;
using System.Activities.Presentation;
using System.Activities.Presentation.Model;
using System.Collections.Generic;
using System.Linq;
namespace AttachedPropertiesBlogPosting
{
class Program
{
static void
{
EditingContext ec = new EditingContext();
ModelTreeManager mtm = new ModelTreeManager(ec);
mtm.Load(new object[] { new Dog { Name = "Sasha", Noise = "Snort", Age = 5 },
new Cat { Name="higgs", Noise="boom", Age=1 } });
dynamic root = mtm.Root;
dynamic dog = root[0];
dynamic cat = root[1];
ModelItem dogMi = root[0] as ModelItem;
ModelItem catMi = root[1] as ModelItem;
// Add an attached Property
AttachedProperty<bool> ap = new AttachedProperty<bool>
{
IsBrowsable = true,
Name = "IsAnInterestingDog",
Getter = (mi => mi.Properties["Name"].ComputedValue.ToString() == "Sasha"),
OwnerType = typeof(Dog)
};
ec.Services.Publish<AttachedPropertiesService>(new AttachedPropertiesService());
AttachedPropertiesService aps = ec.Services.GetService<AttachedPropertiesService>();
aps.AddProperty(ap);
Console.WriteLine("---- Enumerate properties on dog (note new property)----");
dogMi.Properties.ToList().ForEach(mp => Console.WriteLine(" Property : {0}", mp.Name));
Console.WriteLine("---- Enumerate properties on cat (note no new property) ----");
catMi.Properties.ToList().ForEach(mp => Console.WriteLine(" Property : {0}", mp.Name));
AttachedProperty<bool> isYoungAnimal = new AttachedProperty<bool>
{
IsBrowsable = false,
Name = "IsYoungAnimal",
Getter = (mi => int.Parse(mi.Properties["Age"].ComputedValue.ToString()) < 2)
};
aps.AddProperty(isYoungAnimal);
// expect to not see isYoungAnimal show up
Console.WriteLine("---- Enumerate properties on dog (note isYoungAnimal doesn't appear )----");
dogMi.Properties.ToList().ForEach(mp => Console.WriteLine(" Property : {0}", mp.Name));
Console.WriteLine("---- Enumerate properties on cat (note isYoungAnimal doesn't appear )----");
catMi.Properties.ToList().ForEach(mp => Console.WriteLine(" Property : {0}", mp.Name));
Console.WriteLine("---- get attached property via GetValue ----");
Console.WriteLine("getting non browsable attached property on dog {0}", isYoungAnimal.GetValue(dogMi));
Console.WriteLine("getting non browsable attached property on cat {0}", isYoungAnimal.GetValue(catMi));
// now, let's do something clever with the setter.
Console.WriteLine("---- let's use the setter to have some side effect ----");
isYoungAnimal.Setter = ((mi, val) => { if (val) { mi.Properties["Age"].SetValue(10); } });
isYoungAnimal.SetValue(cat, true);
Console.WriteLine("cat's age now {0}", cat.Age);
// now, let's have a browesable one with a setter.
// this plus dynamics are a mini "macro language" against the model items
List<Object> FavoriteAnimals = new List<object>();
// we maintain state in FavoriteAnimals, and use the getter/setter func
// in order to query or edit that collection. Thus changes to an "instance"
// are tracked elsewhere.
AttachedProperty<bool> isFavoriteAnimal = new AttachedProperty<bool>
{
IsBrowsable = false,
Name = "IsFavoriteAnimal",
Getter = (mi => FavoriteAnimals.Contains(mi)),
Setter = ((mi, val) =>
{
if (val)
FavoriteAnimals.Add(mi);
else
{
FavoriteAnimals.Remove(mi);
}
})
};
aps.AddProperty(isFavoriteAnimal);
dog.IsFavoriteAnimal = true;
// remove that cat that isn't there
cat.IsFavoriteAnimal = false;
cat.IsFavoriteAnimal = true;
cat.IsFavoriteAnimal = false;
Console.WriteLine("Who are my favorite animals?");
FavoriteAnimals.ForEach(o => Console.WriteLine((o as ModelItem).Properties["Name"].ComputedValue.ToString()));
Console.ReadLine();
}
}
public class Dog
{
public string Name { get; set; }
public string Noise { get; set; }
public int Age { get; set; }
}
public class Cat
{
public string Name { get; set; }
public string Noise { get; set; }
public int Age { get; set; }
}
}
还可以用XAML的方式来显示属性值:
增加一个下面的Helper:
public class Comment
{
static AttachableMemberIdentifier CommentTextName = new AttachableMemberIdentifier(typeof(Comment), "CommentText");
public static object GetCommentText(object instance)
{
object viewState;
AttachablePropertyServices.TryGetProperty(instance, CommentTextName, out viewState);
return viewState;
}
public static void SetCommentText(object instance, object value)
{
AttachablePropertyServices.SetProperty(instance, CommentTextName, value);
}
}
代码如下:
AttachableMemberIdentifier ami = new AttachableMemberIdentifier(typeof(Comment), "CommentText");
Dog newDog = new Dog { Age = 12, Name = "Sherlock", Noise = "Hooowl" };
AttachablePropertyServices.SetProperty(newDog, ami, "A very good dog");
string s = XamlServices.Save(newDog);
Console.WriteLine("XAML");
Console.WriteLine(s);
Dog aSecondNewDog = XamlServices.Load(new StringReader(s)) as Dog;
string outter;
AttachablePropertyServices.TryGetProperty(aSecondNewDog, ami, out outter);
Console.WriteLine("read out: {0}", outter);
输出:
XAML
<Dog
Age="12"
Comment.CommentText="A very good dog"
Name="Sherlock"
Noise="Hooowl" xmlns="clr-namespace:AttachedPropertiesBlogPosting;assembly=AttachedPropertiesBlogPosting" />
read out: A very good dog
代码如下:
AttachedProperty<string> Comment = new AttachedProperty<string>
{
IsBrowsable = true,
Name = "Comment",
Getter = (mi =>
{
string temp;
AttachablePropertyServices.TryGetProperty<string>(mi.GetCurrentValue(), ami, out temp);
return temp;
}),
Setter = ((mi, val) => AttachablePropertyServices.SetProperty(mi.GetCurrentValue(), ami, val))
};
aps.AddProperty(Comment);
dogMi.Properties["Comment"].SetValue("I think I like that dog");
string xaml = XamlServices.Save(dogMi.GetCurrentValue());
Console.WriteLine("XAML");
Console.WriteLine(xaml);
Console.ReadLine();
输出:
<Dog
Age="5"
Comment.CommentText="I think I like that dog"
Name="Sasha"
Noise="Snort" xmlns="clr-namespace:AttachedPropertiesBlogPosting;assembly=AttachedPropertiesBlogPosting" />
代码来源: