Здравствуйте, Al-Ko, Вы писали:
AK>Здравствуйте, VladD2, Вы писали:
AK>Перед проектированием остановимся на основных возможностях VirtualGrid и его элементов:
че-то у нас обсуждение как-то заглохло. Влад, что делаем дальше?
Здравствуйте, Al-Ko, Вы писали:
AK>че-то у нас обсуждение как-то заглохло.
праздники . Я вот на выходных че-нить попробую осмылить и изложить AK>Влад, что делаем дальше?
Мне кажется следующим пунктом должно быть выделение основных абстракций с из обязанностями и, возможно, примерным интерфейсом. Главное щас четко разделить обязанности между абстракциями и определить между ними связи
Здравствуйте, VladD2, Вы писали:
VD>По-моему давно пора переходить к проектированию интерфейсов и пребным реализациям.
угу, согласен. Я вот и собирался к этому перейти на прошлых выходных, но праздники спутали все планы. А сейчас у меня неделя запарочная. Так что я только с понедельника
Пожалуй пора продолжить . Я тут приведу некие наброски аля первые мысли...
Итак, проведем инвентаризацию, что у нас имеется :
1. абстрактный VirtualGrid — связывает в единый механизм все остальные абстракции. Сам по себе умеет очень мало.
public abstract class VirtualGrid : Control
{
public VirtualGrid() {}
private bool _RowsHeightConst = true;
public bool RowsHeightConst
{
get { return _RowsHeightConst; }
set { _RowsHeightConst = value; }
}
private bool _ColumnsHeightConst = false;
public bool ColumnsHeightConst
{
get { return _ColumnsHeightConst; }
set { _ColumnsHeightConst = value; }
}
private VGColumnCollection _Columns = new VGColumnCollection();
public VGColumnCollection Columns
{
get { return _Columns; }
set { _Columns = value; }
}
private IVGDataSource _DataSource = null;
public IVGDataSource DataSource
{
get { return _DataSource; }
set { _DataSource = value; }
}
private VGHorScroller _HorScroller;
public HorScroller HorScroller
{
get { return _HorScroller; }
set { _HorScroller = value; }
}
private VGVertScroller _VertScroller;
public VGVertScroller VertScroller
{
get { return _VertScroller; }
set { _VertScroller = value; }
}
}
2. Интерфейс источника данных IVGDataSource — знает количество строк, предоставляет по запросу данные.
public interface IVGDataSource
{
int RowsCount { get; }
object GetData(int Row);
event EventHandler DataChanged;
}
3. Колонка VGColumn и коллекция колонок VGColumnCollection
public class VGColumn
{
public VGColumn()
{
}
private string _Name;
public string Name
{
get {return _Name; }
set { _Name = value; }
}
private string _Title;
public string Title
{
get { return _Title; }
set { _Title = value; }
}
}
public class VGColumnCollection : CollectionBase
{
public VGColumnCollection()
{
}
public VGColumn this[int index]
{
get { return (VGColumn)List[index]; }
set { List[index] = value; }
}
public VGColumn this[string Name]
{
get { return null; }
set { }
}
}
4. Строка VGRow и коллекция строк VGRowCollection (на сколько я понимаю, наш грид полностью симметричен относительно колонок/строк)
5. Скроллеры. Имеется некий абстрактный класс скроллеров, вертикальный и горизонтальный скроллеры наследуются от него
public abstract class VGScroller
{
public VGScroller()
{
}
protected int _MaxSize;
public int MaxSize
{
get { return _MaxSize; }
set { _MaxSize = value; }
}
protected int _MinSize;
public int MinSize
{
get { return _MinSize; }
set { _MinSize = value; }
}
protected int _Value;
public int Value
{
get { return _Value; }
set
{
_Value = value;
OnValueChanged();
}
}
public event EventHandler ValueChanged;
protected void OnValueChanged()
{
if (ValueChanged != null)
ValueChanged(this, new EventArgs());
}
}
public class VGGorScroller : VGScroller { /*...*/ }
public class VGVerScroller : VGScroller { /*...*/ }
6. Некая абстракция для управления фокусом и выделением...
ok, давай поэтапно.
SAV> public abstract class VirtualGrid : Control SAV> {
1. От чего насследуется VirtualGrid.
Если мы наследуемся от Control'а, то нам для обеспечения скроллинга нужно будет создавать свои контролы скроллбаров и лепить их к краям контрола.
Преимущества такого подхода: не нужно лезть в WndProc и ловить WM_xSCROLL сообщения для окна, а обрабатывать только события Scroll этих скроллбаров. Недостатки: скроллбары будут находиться в клиентской области окна, поэтому придется каждый раз при вычислениях это учитывать; при одновременном появлении двух скроллбаров нужно лепить в правый нижний угол квадратик-панельку, для того, чтобы там не было дырки .
Если наследоваться от ScrollableControl или его наследников, то таких проблем не будет, но нужно будет работать с сообщениями окна.
Короче, вроде как мы с Владом определились отталкиваться от второго варианта. Только надо вынести Scroller в отдельный класс, который потом можно будет заменить или изменить.
2. Интерфейс IVirtualGrid.
Пока остановимся на этом:
interface IVirtualGrid
{
int RowsCount
{
get;
set;
}
int TopRow
{
get;
set;
}
bool CacheRowsHeight
{
get;
set;
}
int GetBottomRow();
}
к этому интерфейсу будет обращаться грид-наследник.
Дальше. Это частный случай, если строки и столбцы имеют неизменные размеры, это ОК:
SAV> private bool _RowsHeightConst = true; SAV> private bool _ColumnsHeightConst = false;
это здесь не нужно:
SAV> private IVGDataSource _DataSource = null; SAV> public IVGDataSource DataSource SAV> { SAV> get { return _DataSource; } SAV> set { _DataSource = value; } SAV> }
скроллеры пока убираем: SAV> private VGHorScroller _HorScroller; SAV> }
ага, это ты пытался делать нечто вроде интерфейса IVirtualGrid.
SAV>[/c#] SAV>2. Интерфейс источника данных IVGDataSource — знает количество строк, предоставляет по запросу данные. SAV>
Это не нужно, с данными работает наследник VirtualGrid.
3. Теперь о столбцах и их коллекции.
Надо решить, делать ли Column компонентом (его наследником), как предложил Михалик (как DataGridColumnStyle), или не делать этого.
После этого нужно двигаться дальше.
Здравствуйте, Al-Ko, Вы писали:
AK>Если наследоваться от ScrollableControl или его наследников, то таких проблем не будет, но нужно будет работать с сообщениями окна. AK>Короче, вроде как мы с Владом определились отталкиваться от второго варианта. Только надо вынести Scroller в отдельный класс, который потом можно будет заменить или изменить.
Да я вроде тоже собирался его от ScrollableControl унаследовать, запарил
AK>это здесь не нужно:
возможно...
AK>Это не нужно, с данными работает наследник VirtualGrid.
может быть имеет смысл общие методы работы с данными вынести в VirtualGrid? что бы можно было унифицированно работать с разными гридами
AK>Надо решить, делать ли Column компонентом (его наследником), как предложил Михалик (как DataGridColumnStyle), или не делать этого.
Я большого смысла в этом не вижу
AK>После этого нужно двигаться дальше.
двигаемся дальше
Здравствуйте, Al-Ko, Вы писали:
AK>Если мы наследуемся от Control'а, то нам для обеспечения скроллинга нужно будет создавать свои контролы скроллбаров и лепить их к краям контрола.
Так и придётся делать.
AK>Если наследоваться от ScrollableControl или его наследников, то таких проблем не будет, но нужно будет работать с сообщениями окна.
Так не получится. Как только у тебя появятся хидеры колонок, ты поимеешь кучу проблем с ScrollableControl, не говоря уж о тех проблемах, которые в нём есть от рождения — очень уж плохо управляется там скроллингом... Я пробовал и так и эдак, и через интероп — складывать скроллеры как внутренние контролы всяко удобнее.
Здравствуйте, Al-Ko, Вы писали:
AK>Надо решить, делать ли Column компонентом (его наследником), как предложил Михалик (как DataGridColumnStyle), или не делать этого.
Делать. Если у колонки появятся всякие события, например, HeaderRightClick, Sort или еще чего — тебе будет сложновато подписаться на эти события из дизайнера — придётся писать свой хитрый редактор коллекции с подпиской на сообщения, ну или делать это из кода. Кроме того, всё таки
// удобнее писать
clmName.ForeColor = Color.Red;
// чем
grid.Columns["Name"].ForeColor = Color.Red;
Только не забыть поставить [DesignTimeVisible(false)], чтобы в Component Tray они не болтались почём зря.
Здравствуйте, orangy, Вы писали:
O>Здравствуйте, Al-Ko, Вы писали:
AK>>Надо решить, делать ли Column компонентом (его наследником), как предложил Михалик (как DataGridColumnStyle), или не делать этого. O>Делать.
А получится ли нормально DataGridColumnStyle воткнуть в наш грид? чет я не думаю. хотя у меня уже щас сображалка плохо втыкает, надо завтра подумать
Здравствуйте, SiAVoL, Вы писали:
AK>>>Надо решить, делать ли Column компонентом (его наследником), как предложил Михалик (как DataGridColumnStyle), или не делать этого. O>>Делать. SAV>А получится ли нормально DataGridColumnStyle воткнуть в наш грид? чет я не думаю. хотя у меня уже щас сображалка плохо втыкает, надо завтра подумать
Нет, ну прямо DataGridColumnStyle не надо втыкать, там интерналы есть. Надо свой класс колонки написать. Согласно архитектуре.
AK>>>Надо решить, делать ли Column компонентом (его наследником)
тьфу, блин. Прочитал неправильно Компонентом надо делать SAV>щас сображалка плохо втыкает
все, пора уходить в плановый запой — расслабляться (за подробностями сюда
O O>Так не получится. Как только у тебя появятся хидеры колонок, ты поимеешь кучу проблем с ScrollableControl, не говоря уж о тех проблемах, которые в нём есть от рождения — очень уж плохо управляется там скроллингом... Я пробовал и так и эдак, и через интероп — складывать скроллеры как внутренние контролы всяко удобнее.
ScrollableControl нужен только для того, чтобы обеспечить наличие скроллбаров в клиентской области и также генерирования сообщений скроллинга для окна. После этого весь его ненавязчивый сервис надо попытаться отключить. Технологией с отдельными скроллбарами-контролами я владею, а вот хотелось бы попробовать именно с сабжем. А что, есть печальный опыт, который говорит что это не получится?
Здравствуйте, orangy, Вы писали: O>Делать. Если у колонки появятся всякие события, например, HeaderRightClick, Sort или еще чего — тебе будет сложновато подписаться на эти события из дизайнера — придётся писать свой хитрый редактор коллекции с подпиской на сообщения, ну или делать это из кода. Кроме того, всё таки O>[c#] O>// удобнее писать O>clmName.ForeColor = Color.Red;
Здравствуйте, Al-Ko, Вы писали:
AK>ScrollableControl нужен только для того, чтобы обеспечить наличие скроллбаров в клиентской области
Картинка такая на мой взгляд должна быть:
Т.е. хидеры не должны скроллиться, и скроллер не должен продолжаться на них. Второе еще можно игнорировать, но первое нельзя. А вот ScrollableControl — штука хитрая, он будет "оптимизировать" скроллинг, т.е. двигать битмапу, которую ты нарендерил на область контрола. И сначала скроллить её, а потом просить тебя нарисовать остаток. Не связывайтесь с ним, кривой он.
AK>и также генерирования сообщений скроллинга для окна.
Долго ли подписаться на события от VScrollBar? Там надо-то на ValueChanged подписаться да и всё. Ну и при отрисовке учитывать его размер.
Здравствуйте, orangy, Вы писали:
O>Здравствуйте, Al-Ko, Вы писали:
AK>>ScrollableControl нужен только для того, чтобы обеспечить наличие скроллбаров в клиентской области O>Картинка такая на мой взгляд должна быть:
O>Т.е. хидеры не должны скроллиться,
безусловно O>и скроллер не должен продолжаться на них.
должен — посмотри на любой грид, да хоть бы на тот, что в Хоуме O>Второе еще можно игнорировать, но первое нельзя. А вот ScrollableControl — штука хитрая, он будет "оптимизировать" скроллинг, т.е. двигать битмапу, которую ты нарендерил на область контрола. И сначала скроллить её, а потом просить тебя нарисовать остаток. Не связывайтесь с ним, кривой он.
а если ему ноги выдрать, обрабатывая WM_xSCROLL, все равно будет скроллить? Не пробовал, если честно, но надо будет заняться.
O>Долго ли подписаться на события от VScrollBar? Там надо-то на ValueChanged подписаться да и всё.
боюсь, что придется подписаться для каждого скроллбара на Scroll event и обрабатывать типы этого события SmallIncrement/Decrement, LargeIncrement/Decrement, ThumbTrack, а также еще события MouseWheel. O>Ну и при отрисовке учитывать его размер.
Да, в этом случае нужно при ресайзинге контрола, а также при изменениях в строках/столбцах формировать "свой" client rectangle с учетом размеров скроллбаров, и все события мыши, скроллинга и отрисовку контролировать и осуществлять в нем.
Почему-то Влад выступает против этого способа. См.Re[12]: Completely managed TreeViewControl
Это не очень принципиально, я же написал. Но всё-таки мне кажется, что полосы прокрутки должны быть вокруг того, что прокручивается. Возьми, к примеру это окно (окно написания сообщения в янусе). Тут тоже есть Header и Message Body. Я думаю, тебя бы сильно удивило, если бы скроллер был бы от верха хидера до низа тела письма
O>>Второе еще можно игнорировать, но первое нельзя. А вот ScrollableControl — штука хитрая, он будет "оптимизировать" скроллинг, т.е. двигать битмапу, которую ты нарендерил на область контрола. И сначала скроллить её, а потом просить тебя нарисовать остаток. Не связывайтесь с ним, кривой он. AK>а если ему ноги выдрать, обрабатывая WM_xSCROLL, все равно будет скроллить? Не пробовал, если честно, но надо будет заняться.
А смысл тогда им пользоватся?
O>>Долго ли подписаться на события от VScrollBar? Там надо-то на ValueChanged подписаться да и всё. AK>боюсь, что придется подписаться для каждого скроллбара на Scroll event и обрабатывать типы этого события SmallIncrement/Decrement, LargeIncrement/Decrement, ThumbTrack, а также еще события MouseWheel.
Не придётся. По-крайней мере, мне не пришлось в моём виртуальном деревянном гриде
O>>Ну и при отрисовке учитывать его размер. AK>Да, в этом случае нужно при ресайзинге контрола, а также при изменениях в строках/столбцах формировать "свой" client rectangle с учетом размеров скроллбаров, и все события мыши, скроллинга и отрисовку контролировать и осуществлять в нем.
Что-то вы, батенька, усложняете При нормально прописанной структуре контрола это элементарно всё...
AK>Почему-то Влад выступает против этого способа. См.Re[12]: Completely managed TreeViewControl
O>Это не очень принципиально, я же написал. Но всё-таки мне кажется, что полосы прокрутки должны быть вокруг того, что прокручивается. Возьми, к примеру это окно (окно написания сообщения в янусе). Тут тоже есть Header и Message Body. Я думаю, тебя бы сильно удивило, если бы скроллер был бы от верха хидера до низа тела письма
ну мы же про грид говорим.
O>>>Второе еще можно игнорировать, но первое нельзя. А вот ScrollableControl — штука хитрая, он будет "оптимизировать" скроллинг, т.е. двигать битмапу, которую ты нарендерил на область контрола. И сначала скроллить её, а потом просить тебя нарисовать остаток. Не связывайтесь с ним, кривой он. AK>>а если ему ноги выдрать, обрабатывая WM_xSCROLL, все равно будет скроллить? Не пробовал, если честно, но надо будет заняться. O>А смысл тогда им пользоватся?
скроллбары не будут входить в ClientRectangle...
(касательно скроллбаров) и ветки ниже.
Почитал ту ветку (честно скажу, по диагонали). Во многом не согласен с Владом, ну да это как обычно Например, я считаю абсолютно неверным рассчёт во время отрисовки. Мало того, что реакция контрола на требование перерисоваться замедляется, так еще и программно с ним работать сложнее становится. Прикинь на досуге реализацию функций:
int GetRowAt(int x, int y); // для хит-тестов разных, например для драг-дропаint GetRowBottom(int index); // для выпадающего меню, чтобы строку не загораживалоvoid ScrollRowIntoView(int index); // для программного показа строки, например свежедобавленой
Конечно, это можно реализовать и так, и эдак, но всё-таки мне кажется, что лучше иметь готовый рассчитанный layout. Да, не забудь про резиновые колонки, это очень полезная функция. Я имею ввиду возможность задавать не только фиксированную ширину (в пикселях), но и в процентах или в весах.
Что касается ScrollableControl и иже с ними... Попробуй с этим чудом поработать, сам поймёшь. Слишком много думать за программиста пытается. В этом вопросе с Владом я согласен, лучше делать свой скроллирующий контрол, но такой, какой надо. Насчёт же Авалона — я бы с этим пока не заморачивался, рано еще. Т.е. наследование может быть таким: Control -> ScrollableControlEx (это свой) -> VirtualGridBase -> ну и дальше клиенты.
Еще один момент. Не злоупотребляй интерфейсами. Они заставляют клиента (наследника VG) слишком много имплементировать. Например, если строки переменной высоты можно будет ресайзить, то должен быть предусмотрен какой-то механизм сообщения клиенту об этом для сохранения настроек. А если оно совсем не нужно клиенту? Не хотелось бы иметь VG, ради использования которого надо написать еще кучу кода, причём вида return false; или return null; Вообще не очень пониманию, зачем при общении базового класса с производным нужны какие бы то ни было интерфейсы, и так средств достаточно. Ну да вам виднее, вы уже много про это думали Удачи!