Отображение нескольких одновременных видов одного и того же документа - PullRequest
5 голосов
/ 07 апреля 2011

Как убедить архитектуру MFC Doc / View позволить мне одновременно отображать два разных вида одного и того же документа?

Например, скажем, мой подкласс CDocument представляет архив некоторого описания.
Мне нужен пользовательский интерфейс, в котором имена всех записей в этом архиве представлены в подклассе CListView на левой панели, а сведения о выбранной в данный момент записи отображаются в подклассе CEditView.класс в правой панели.

Кажется, что CSingleDocTemplate позволяет подключать только один документ, один фрейм и один вид.Я все еще хочу приложение SDI, но мне нужен один документ и два разных представления - не в этом ли смысл хорошей архитектуры Doc / View?

Ответы [ 3 ]

7 голосов
/ 07 апреля 2011

SDI означает «Один Документ Интерфейс», он ограничивает вас только одним документом за раз, но не числом представлений, которые вы можете открыть для этого документа.

Вероятно, наиболее распространенным подходом для открытия нескольких представлений в приложении SDI являются Windows Splitter .

Вы добавляете один вид к CSingleDocTemplate (не важно, какой)

pDocTemplate = new CSingleDocTemplate(
    IDR_MYRESOURCEID,
    RUNTIME_CLASS(CMyDoc),
    RUNTIME_CLASS(CMyFrameWnd),
    RUNTIME_CLASS(CMyListView));

Ваше окно кадра получает экземпляр CSplitterWnd m_wndSplitter, и вы перегружаете виртуальную функцию OnCreateClient:

BOOL CMyFrameWnd::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) 
{
    VERIFY(m_wndSplitter.CreateStatic(this,1,2)); // one row / two columns

    VERIFY(m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CMyListView),
        CSize(300,300),pContext));
    VERIFY(m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CMyEditView),
        CSize(300,300),pContext));

    return TRUE;
}

В этом примере создается окно разделителя с одной строкой и двумя столбцами. С левой стороны в разделителе вид вида CMyListView, а с правой стороны вид вида CMyEditView.

Вы даже можете вкладывать несколько окон-разделителей друг в друга для создания произвольных наборов сложных представлений в рамочном окне.

Вот небольшой учебник, который показывает, как работать с окнами-разделителями в приложении SDI:

http://www.codeproject.com/KB/splitter/splitterwindowtutorial.aspx

Редактировать

Соединение представлений, которые вы добавляете в разделитель с документом, делает MFC внутренним: CCreateContext* pContext, который передается в OnCreateClient, содержит ссылку m_pCurrentDoc на текущий документ (Framewindow знает об этом документе). MFC использует это в CView::OnCreate (ViewCore.cpp), чтобы добавить представление к документу: m_pCurrentDoc->AddView(this) и установить указатель документа m_pDocument в представлении.

Поэтому последующие вызовы UpdateAllViews вашего документа позаботятся об обоих видах.

2 голосов
/ 07 апреля 2011

Пересмотрено на основе комментария:

Хорошо, вам нужно статическое окно сплиттера.Самый простой способ (я знаю) создать это - начать с проекта SDI MFC и сообщить ему, что вам нужно окно разделителя (в AppWizard в разделе «Функции интерфейса пользователя» установите флажок «Разделить окно»).Это создаст динамический разделитель - то есть, он начинается только с одной панели, и вы можете создать секунду, перетаскивая панель разделителя - но когда вы это сделаете, вы просто получите два одинаковых представления (хотя вы можете прокрутить их отдельнодруг от друга).

Затем нам нужно проделать небольшую работу, чтобы превратить ее из динамического разделителя в статический разделитель.Вероятно, лучше начать с просмотра кода для динамического сплиттера.Если вы посмотрите в CMainFrame этого приложения, вы обнаружите, что оно имеет:

CSplitterWnd m_wndSplitter;

Если вы посмотрите в OnCreateClient основного кадра, вы найдете что-то вроде этого:

return m_wndSplitter.Create(this,
    2, 2,               // TODO: adjust the number of rows, columns
    CSize(10, 10),      // TODO: adjust the minimum pane size
    pContext);

Это то, что нам нужно изменить - Create - это то, что создает динамический сплиттер.Мы должны избавиться от этого и создать вместо этого статический сплиттер.Первый шаг для этого - создать другой класс представлений - сейчас у нас есть только один класс представлений, но мы хотим два, по одному для каждой панели.

Самый простой (из известных мне) способ созданиянаш второй класс представления должен запустить вторую копию VS и создать другое (отдельное) приложение.Мы расскажем ему, чтобы основать класс представления для этого приложения на CListView.Затем мы возьмем файлы для этого представления и добавим их в наш оригинальный проект.Чтобы упростить процесс подключения, мы хотим убедиться, что этот второй проект использует то же имя для своего класса документа, что и первый.

На этом этапе у нас есть код для нашего второго представления., но он не связан с чем-либо еще, поэтому созданное им представление не будет видимо.Чтобы сделать его видимым, нам нужно включить его заголовок в CMainframe.cpp (или любое другое имя, которое он имеет в вашем целевом проекте).Затем мы возвращаемся к OnCreateClient и заменяем приведенный выше код на что-то вроде этого:

CRect rect;
GetClientRect(&rect);

BOOL ret = m_wndSplitter.CreateStatic(this, 2, 1); // 2 rows, 1 column of views

    // row 0, column 0 will be the "OriginalView". The initial split will be 
    // in half -- i.e., each pane will be half the height of the frame's client
    //
m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(OriginalView), CSize(1, rect.Height()/2), pContext);
m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(ListBasedView), CSize(1, rect.Height()/2), pContext);

На данный момент я создал горизонтальное разделение с «OriginalView» в верхнемпанель и «ListBaseView» в нижней панели, но (я думаю) должно быть совершенно очевидно, какие изменения нужно внести, чтобы изменить порядок представлений.

Оттуда, конечно, вам придется написатькод в каждого представления, чтобы делать то, что предполагается, что это представление должно делать - но так как каждое все еще является отдельным, нормальным представлением, каждое является достаточно независимым, поэтому разработка примерно такая же, как нормальная.Единственное существенное отличие состоит в том, что вы должны следовать правилам признания документа недействительным, и (особенно если обновление одного из представлений является дорогостоящим) вы можете рассмотреть возможность использования подсказок, чтобы указать, какая часть данных имеетбыл признан недействительным, поэтому вы можете написать каждое представление для обновления только того, что нужно, вместо того, чтобы просто перерисовывать все его данные каждый раз.

1 голос
/ 07 апреля 2011

Может быть, эта статья, Несколько представлений с использованием SDI , - это то, что вам нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...