Известно, что в текущей версии WPF существует проблема с качеством отображения растров. Возникает она из-за того, что WPF отображает визуальные элементы исходя из координат и размеров в логических единицах (долях дюйма) без точной привязки к пикселам. То есть, по умолчанию, растровое изображение в WPF-приложении будет иметь одинаковый размер в логических единицах на мониторе с разрешением 96 DPI и на супер-мониторе с разрешением 300 DPI. Теоретически это звучит хорошо, но на практике дело обстоит иначе.
Растровые изображения являются основным элементом пиксельной графики. Это означает, что эти избражения тесно связаны с пиксельной природой устройств, для использования на которых они предназначены. Точно также, как игра в «морской бой» связана с клетками на тетрадном листе. Таким образом, если начать играть в независимость пиксельных изображений от собственно пикселей, мы получим размытую картинку на мониторе. Что мы и имеем в современных WPF-приложениях.
На самом деле, есть случаи, когда такое положение вещей допустимо. Например, растянутая на фоне элемента управления текстура с размытыми цветами, и тому подобное. Но в большинстве случаев, когда растры используются для картинок в кнопках на панелях управления, меню (16х16, 32х32 пикселя), и так далее, это не годится; картинки размыты, приложение неумолимо теряет профессиональный вид.
Это не устраивает многих разработчиков, и обсуждение этой темы обычно заканчивается бессильными протестами в сторону создателей платформы. Но, к счастью, MSDN-блоггером было предложено решение этой проблемы. В статье описывается класс Bitmap, который позволяет отображать растры с точной пиксельной привязкой к целевому устройству. Используйте этот класс, и ваши растры будут вылядеть как родные! При этом, естественно на супер-мниторе с 300 DPI ваша картинка будет выглядеть крошечной. Но на то они и растры, я считаю. Примяняйте векторные изображения, и с масштабированием все будет отлично.
Прекрасно, но существует проблема, которая не позволяет использовать этот класс в случаях, когда нужно осущеcтвить привязку к данным через DataContext. Я столкнулся с этим при написании CellTemplate для ListView. Предполагалось, что ячейка будет содержать растровое избражение, привязанное к пикселам.
Но это, к сожалению, не работало. Возникало исключение, смысл которого в том, что наследование DataContext работает только для классов, производных от FrameworkElement. В то же время, наш американский товарищ унаследовал свой класс Bitmap от UIElement, потому что только так можно перекрыть методы, которые являются sealed для наследников FrameworkElement.
Использовать класс очень хотелось, и мне пришлось сделать обертку вокруг Bitmap, порожденную от FrameworkElement. Для того, чтобы это выглядело прозрачно, я перекрыл визуальное дерево элемента и встроил в него реализацию, выполненную ранее. Моя обертка тоже называется Bitmap, а внутренний класс, который выполняет собственно привязку к пикселам, я переименовал в BitmapUIElement.
Чтобы проиллюстрировать все это, я сделал небольшой пример. В нем используется ListView для отображения всех файлов из папки System32 – имена и иконки 32х32. Обратите внимание, что иконки выглядят так же качественно, как в Explorer.
Вот, собственно, и все. В заключение еще раз скажу, что если у вас есть возможность использовать векторные изображения, то лучше используйте их.