У кого-нибудь есть хороший пример, как глубоко клонировать объект WPF, сохраняя привязки данных?
Отмеченный ответ - первая часть.
Вторая часть заключается в том, что вам нужно создать ExpressionConverter и внедрить его в процесс сериализации. Подробности здесь:
http://www.codeproject.com/KB/WPF/xamlwriterandbinding.aspx?fid=1428301&df=90&mpp=25&noise=3&sort=Position&view=Quick&select=2801571





Как насчет:
public static T DeepClone<T>(T from)
{
using (MemoryStream s = new MemoryStream())
{
BinaryFormatter f = new BinaryFormatter();
f.Serialize(s, from);
s.Position = 0;
object clone = f.Deserialize(s);
return (T)clone;
}
}
Конечно, это глубокое клонирование любого объекта, и, возможно, это не самое быстрое решение в городе, но оно требует минимального обслуживания ... :)
Самый простой способ, который я сделал, - использовать XamlWriter для сохранения объекта WPF в виде строки. Метод Save сериализует объект и все его дочерние элементы в логическом дереве. Теперь вы можете создать новый объект и загрузить его с помощью XamlReader.
бывший: Запишите объект в xaml (скажем, объект был элементом управления Grid):
string gridXaml = XamlWriter.Save(myGrid);
Загрузите его в новый объект:
StringReader stringReader = new StringReader(gridXaml);
XmlReader xmlReader = XmlReader.Create(stringReader);
Grid newGrid = (Grid)XamlReader.Load(xmlReader);
Я не думаю, что это сохраняет анимацию, не так ли?
Чтобы было ясно, это только половина решения (как это было в 08). Это приведет к оценке привязок и сериализации результатов. Если вы хотите сохранить привязки (как заданный вопрос), вы должны либо добавить ExpressionConverter к типу привязки во время выполнения (см. Вторую часть моего вопроса для соответствующей ссылки), либо увидеть мой собственный ответ ниже, чтобы узнать, как это сделать в 4.0 .
Удивительно просто идеально :)
Жаль, что XamlWriter такой медленный. Жаль, что не было альтернативы.
не работает с элементами управления, использующими привязку к универсальным типам.
В .NET 4.0 новый стек сериализации xaml делает это НАМНОГО проще.
var sb = new StringBuilder();
var writer = XmlWriter.Create(sb, new XmlWriterSettings
{
Indent = true,
ConformanceLevel = ConformanceLevel.Fragment,
OmitXmlDeclaration = true,
NamespaceHandling = NamespaceHandling.OmitDuplicates,
});
var mgr = new XamlDesignerSerializationManager(writer);
// HERE BE MAGIC!!!
mgr.XamlWriterMode = XamlWriterMode.Expression;
// THERE WERE MAGIC!!!
System.Windows.Markup.XamlWriter.Save(this, mgr);
return sb.ToString();
Я не понимаю, чем это отличается от простого использования XamlWriter.Save? По крайней мере, я не увидел других результатов при попытке сериализовать DataGrid.
@JP извините, не все так ясно .... Вопрос был в том, как клонировать сохраняя привязки. Отмеченный ответ наполовину правильный; фактически, если вы это сделаете, вы обнаружите, что ваши привязки будут оценены, и результаты (а не сами привязки) будут сериализованы. В моем вопросе я добавил к решению вторую половину, а именно: добавить ExpressionConverter и добавить его к типу привязки во время выполнения. Это немного непонятно. То же самое можно сделать с помощью этого ответа - см. Комментарий HERE BE MAGIC? Это указывает сериализатору нет оценивать привязки при сериализации. Аккуратный.
Я видел это. Я все еще работаю над той же проблемой с моим Datagrid и не заметил другого результата в отношении привязки данных. Мой Datagrid все еще пуст. Я, должно быть, упускаю что-то еще. Тем не менее, я дам вам голос за то, что вы указали на все это.
@JPRichardson: сериализуйте текст и проверьте результат, чтобы увидеть, сохранены ли {Binding}.
Я продаю его как полезный, я пишу форму динамического ввода данных, и это решило мою проблему привязки. Спасибо.
Здесь есть отличные ответы. Очень полезно. Я пробовал различные подходы для копирования информации о привязке, включая подход, описанный в http://pjlcon.wordpress.com/2011/01/14/change-a-wpf-binding-from-sync-to-async-programatic/, но информация здесь лучшая в Интернете!
Я создал многократно используемый метод расширения для работы с InvalidOperationException «Привязка не может быть изменена после того, как она была использована». В моем сценарии я поддерживал некоторый код, написанный кем-то, и после серьезного обновления инфраструктуры DevExpress DXGrid он больше не работал. Следующее отлично решило мою проблему. Часть кода, в которой я возвращаю объект, могла бы быть лучше, и я перефразирую это позже.
/// <summary>
/// Extension methods for the WPF Binding class.
/// </summary>
public static class BindingExtensions
{
public static BindingBase CloneViaXamlSerialization(this BindingBase binding)
{
var sb = new StringBuilder();
var writer = XmlWriter.Create(sb, new XmlWriterSettings
{
Indent = true,
ConformanceLevel = ConformanceLevel.Fragment,
OmitXmlDeclaration = true,
NamespaceHandling = NamespaceHandling.OmitDuplicates,
});
var mgr = new XamlDesignerSerializationManager(writer);
// HERE BE MAGIC!!!
mgr.XamlWriterMode = XamlWriterMode.Expression;
// THERE WERE MAGIC!!!
System.Windows.Markup.XamlWriter.Save(binding, mgr);
StringReader stringReader = new StringReader(sb.ToString());
XmlReader xmlReader = XmlReader.Create(stringReader);
object newBinding = (object)XamlReader.Load(xmlReader);
if (newBinding == null)
{
throw new ArgumentNullException("Binding could not be cloned via Xaml Serialization Stack.");
}
if (newBinding is Binding)
{
return (Binding)newBinding;
}
else if (newBinding is MultiBinding)
{
return (MultiBinding)newBinding;
}
else if (newBinding is PriorityBinding)
{
return (PriorityBinding)newBinding;
}
else
{
throw new InvalidOperationException("Binding could not be cast.");
}
}
}
Имейте в виду, что он также клонирует имя, что усложняет их использование для элементов пользовательского интерфейса, если они должны быть размещены в одном корневом контейнере.