skip to main |
skip to sidebar
Что происходит, когда мы вызываем:dictionary[key] = null;
Что произойдёт если ключа "key" нет в Dictionary?
Мой коллега долго искал баг в программе, потому что был уверен, что если в словаре нет ключа "key", то программа вернёт ошибку. Из-за этого он не обращал на этот вызов внимания.
А вот что происходит на самом деле:
Dictionary проверяет наличие ключа и если не находит, то _создаёт_ его! И присваивает указанное значение. В нашем случае получается пара "key; null" .
Мораль сей басни такова: внимательнее обращайтесь со свойствами, реализация которых скрыта! И с Dictionary в частности. =)
Читать дальше......
Задача звучит следующим образом:Есть окошко в котором содержатся редактируемое текстовое поле и кнопка.Внимание, вопрос:Сколько у нас окон? Дайте ответ в контексте WinForms, Win32 и WPF.
Читать дальше......
Если вы когда-нибудь получали сообщение об ошибке с текстом:A 'Binding' cannot be set on the 'Geometry' property of type 'ConnectionByLine'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.То уже знаете, что решение проблемы элементарно до безобразия, хотя некоторые, такие как я, всё же упускают детали.Чаще всего это возникает, когда название свойства (в примере - Geometry) не соответствует регистрируемому свойству: DependencyProperty.Register("Geometry", ...);Обычно DependencyProperty имеет следующий вид:
public static readonly DependencyProperty GeometryProperty = DependencyProperty.Register("Geometry", typeof(PathGeometry), typeof(BaseShape));
public PathGeometry Geometry
{
get { return (PathGeometry)GetValue(GeometryProperty); }
set { SetValue(GeometryProperty, value); }
}
После этого можно производить привязку к данному свойству следующим образом в XAML-коде:
...
Label
Content="{Binding ElementName=MyLine, Path=Geometry}"
...
Но помните, если вы примените какое-нибудь послабление в именовании свойств, то получите ошибку с сообщением о том, что привязку можно производить только на зависимые свойства.
Читать дальше......
Предусловия:WPF, DataGrid, Binding.Вы используете DataGrid, ячейки таблицы меняют свои состояния динамически, и привязаны к неким свойствам посредством Binding.Симптомы:При скролировании строк DataGrid'а, состояния некоторых свойств отображаются неверно.Решение:Чтобы решить проблему достаточно переключить свойство VirtualizingStackPanel.VirtualizationMode на значение "Standard".
Что при этом происходит?
VirtualizingStackPanel используется, как видно из названия, для виртуализации объектов, в нашем случае, DataGrid. Т.е. когда вы смещаете видимую область таблицы, строки, которые не отображаются виртуализируются с целью экономии. Но и тут есть особенности - одно дело виртуализировать объект, другое - его состояния.
Значение свойства "Standard" отключает виртуализацию. Это приводит к тому, что все объекты таблицы хранятся в памяти целиком. Минусы: память расходуется не оптимально, плюсы: не требуется восстанавливать и виртуализировать объекты, и как следствие - работа с объектами происходит быстрее.
Listing...
DataGrid
Style="{StaticResource DataGridStyle}"
MaxWidth="500"
MaxHeight="500"
SelectionMode="Single"
SelectionUnit="FullRow"
Loaded="MainGrid_Loaded"
VirtualizingStackPanel.VirtualizationMode="Standard"
Читать дальше......
Предусловия:
Вы создали свой контрол. В своём контроле создали своё DependencyProperty с объектом коллекции, например ObservableCollection<>:
Listing 1:
public static readonly DependencyProperty ElementPortsProperty = DependencyProperty.Register("CollectedProperty",
typeof(ObservableCollection<>), typeof(MyClass), new FrameworkPropertyMetadata(new ObservableCollection<>());
Проблема:
Возможна следующая ситуация когда вы восстанавливаете такой объект при помощи XamlReader: свойство-коллекция элемента содержит дублированные
элементы.
Пояснение:
Если в контроле есть custom свойство-коллекция, которое заполнено в XAML-файле вашего контрола то в момент дессериализации будет происходить следующее:
Восстановится ваш контрол целиком (с заполненным по-умолчанию свойством-коллекцией), затем в Xaml-файле обнаружится коллекция с объектами и эти объекты добавятся в восстановленный Custom Control.
Как с этим бороться? Два очевидных способа:
1. Не заполнять в Конструкторе коллекцию, а инициализировать её заполнение из .cs-файла когда это нужно.
2. В элементы коллекции добавить уникальный ключ, который позволит проверять наличие этого элемента в коллекции.
Примеры:
Binary of uncorrect working DP
Binary of correct working DP
Sources of correct working DP
Читать дальше......
Что нужно сделать, чтобы доступиться к входным параметрам WPF-приложения?
Классическое WinForms-приложение имеет точку входа следующего вида:
public static void Main(string[] parameters)
где parameters и есть те самые входные параметры.
Чтобы доступиться до входных параметров WPF-приложения, нужно использовать перегрузку метода:OnStartup(StartupEventArgs e)В общем виде ваш стартовый класс может выглядеть так:Listing 1:Listing...
private static string[] _arguments;
public static string[] AppArguments
{
get { return _arguments;
}
public App()
{
InitializeComponent();
}
protected override void OnStartup(StartupEventArgs e)
{
_arguments = new string[e.Args.Length];
for (int i = 0; i <>
{
_arguments[i] = e.Args[i];
}
}
Входные параметры сохраняются в статическом свойстве и будут легко доступны из любой точки приложения.
Примеры:
Source of the example "How to Run WPF Application with parameters".
Binary of the example "How to Run WPF Application with parameters".
Читать дальше......
Чтобы охватить большее количество ситуации, я абстрагируюсь от конкретной ситуации, в которой столкнулся с вопросом восстановления объектов в WPF.Представим, что у вас есть разработанный вами элемент управления, он же UserControl или CustomControl.Часть его может быть описана в XAML файле вашего класса, часть в коде на языке C# (VB).Зачастую мы будем работать с нашими объектами и не встретимся с проблемами, но ситуации всё же возможны.Если в процессе его использования, вы зададите ему именованный Content или измените имя существующего, то это может привести к ошибке, примерно следующего содержания:"Cannot set Name attribute value '' on element ''. '' is under the scope of element 'ParentName', which already had a name registered when it was defined in another scope."Ещё ситуация, когда подобное может произойти: вы анимируете объект или часть объекта. Это требует задавать имена объектам подверженным анимации, и, как следствие, десериализация (Например, Copy/Paste) такого объекта может дать туже ошибку. Яркий пример подобной ситуации - использование InkCanvas и его внутренних методов CopySelection()/Paste(), результатом которых может быть ошибка в случаях когда имя объекта изменялось.Решение:В сети гуляет несколько способов решения этой проблемы, но мне пришлось разрабатывать свой, так как ни один из предложенных не подходил по той или иной причине.
В качестве решения, я решил залезть в буфер обмена и подредактировать данные, которые там содержатся.
Listing 1:
Listing...
public static string ProcessNamedXamlInCilpboard(string mask)
{
if (string.IsNullOrEmpty(mask))
mask = "Name=\"";
string retval = "";
IDataObject provider = Clipboard.GetDataObject();
object xaml = provider.GetData("Xaml", true);
if (xaml == null)
return retval;
retval = xaml as string;
retval = ProcessRemoveMaskedXaml(retval, mask);
try
{
DataObject data = new DataObject(DataFormats.Xaml, retval);
Clipboard.SetDataObject(data, false);
}
catch (Exception E)
{
Console.WriteLine(E.Message);
}
return retval;
}
private static string ProcessRemoveMaskedXaml(string source, string mask)
{
if (!source.Contains(mask))
return source;
int startIndex = source.IndexOf(mask);
int finishIndex = startIndex;
char finish = ' ';
do
{
finish = source[finishIndex];
finishIndex++;
}
while (finish != ' ');
source = source.Remove(startIndex, finishIndex - startIndex);
source = ProcessRemoveMaskedXaml(source, mask);
return source;
}
Первый метод называется ProcessNamedXamlInCilpboard в качестве параметра принимает строку (текст, который надо найти и удалить из описания Xaml), возвращает уже обработанную строку Xaml.
Что происходит в этом методе:
Мы доступаемся до буфера обмена, получаем xaml-код серилизованного объекта, передаём xaml-код и маску непосредственно обработчику.
В методе обработчике:
Проверяем, есть ли необходимость обрабатывать xaml-код, получаем индекс начала маски в xaml-коде, в цикле получаем закрывающий индекс маски, удаляем блок текста из xaml-кода, рекурсивно проверяем наличие маски далее в xaml-коде и возвращаем обработанный код.
Область для оптимизации: поиск маски в xaml-коде.
Дело в том, что производя проверку на наличие в строке маски, мы проходим всю строку, затем ещё раз, когда получаем позицию маски в строке. И так каждый раз, а если маска встречается в конце "миллионсимвольной" строки? Поэтому попробуем сделать следующим образом:
Listing 2:
Listing...
private static string ProcessRemoveMaskedXaml(string source, string mask, int position)
{
//if (!source.Contains(mask))
// return source;
int startIndex = source.IndexOf(mask, position);
if (startIndex == -1)
return source;
int finishIndex = startIndex;
char finish = ' ';
do
{
finish = source[finishIndex];
finishIndex++;
}
while (finish != ' ');
source = source.Remove(startIndex, finishIndex - startIndex);
source = ProcessRemoveMaskedXaml(source, mask, startIndex);
return source;
}
Какие изменения были внесены в код:
Мы убрали проверку на наличие маски в строке, так как нам всё равно надо лезть в неё - будь-то проверка или поиск.
Для поиска используем индекс, откуда начинать искать - это позволит не производить поиск в той части строки, которая уже была проверена. В случае, когда ничего не найдено, мы проверим возвращаемое значение на -1 и обработаем выход из функции.
Обратите внимание на то, что изменилось количество параметров у функции, а соответственно и её вызов:
retval = ProcessRemoveMaskedXaml(retval, mask, 0);
в качестве индекса начала поиска передадим ноль.
P.S. Код не претендует на идеальность и уверен, вы ещё сможете оптимизировать его под свои нужды.
Читать дальше......