Метод переопределения C # во время выполнения - PullRequest
5 голосов
/ 14 августа 2010

У меня два вопроса.

1) Я нашел небольшую жемчужину кода для , как сделать плавную прокрутку управления .

Отлично. Но он переопределяет метод WndProc, поэтому для его использования мне пришлось вырвать FlowLayoutPanel, который я сбросил на форму во время разработки, создать подкласс FlowLayoutPanel, а затем, наконец, создать экземпляр моего нового класса и создать все свойства вручную и изменить все ссылки к элементу управления, чтобы быть этим. Controls ["ControlName"]. (Или, я думаю, я мог бы создать переменную уровня класса, которая по сути была бы тем, чем изначально был элемент управления, хотя как они позволяют вам использовать intellisense, когда он нигде не объявлен?)

Так что теперь мне просто интересно, был ли на самом деле способ сделать это во время выполнения.

Могу ли я сделать что-то простое, как это, где MainPanel - это имя элемента управления:

MainPanel = (SmoothScrollingFlowLayoutPanel)MainPanel

Это не может быть так просто, не так ли? Несмотря на это, это раздражает, потому что у меня все еще должен быть подкласс (который может быть хорошим дизайнерским решением, но я хотел бы свободы, чтобы исключить его). Таким образом, было бы возможно поместить код в родительский элемент FlowLayoutPanel примерно так:

private Delegate void WndProcHandler(ref Message m);
private WndProcHandler w;

public void SomeCode() {
   w = MainPanel.WndProc; // get reference to existing wndproc method
   MainPanel.WndProc = WndProcSmoothScroll; //replace with new method
}

private void WndProcSmoothScroll(ref Message m) { // make smooth scrolling work
   if (
      (m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL)
      && (((int)m.WParam & 0xFFFF) == 5)
   ) {
      m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4);
   }
   if (w != null) { w(); }
   base.WndProc(ref m);
  }

Я понимаю, что это, вероятно, довольно наивно. Я отношусь к методу WndProc как к событию, а не к нему.

2) Итак, мой второй вопрос: если бы WndProc был событием, а не методом, как бы я сделал то же самое - сохранить копию исходного списка обработчиков для события, установить сначала запустить собственный обработчик событий, а затем вызвать все исходные обработчики событий?

ВКУСНЫЕ БИТЫ

В случае, если кому-то интересно, я заметил возможную оптимизацию в коде с плавной прокруткой:

//m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4);
m.WParam = (IntPtr)((int)m.WParam ^ 1);

Поскольку мы хотим перевести последние 16 битов с 5 на 4, мы можем просто перевернуть последний бит (XOR) вместо И, затем ИЛИ.

Ответы [ 2 ]

8 голосов
/ 14 августа 2010

Если я правильно понимаю ваш вопрос, все, что вам нужно, это переопределить WndProc во время выполнения. Если это так, все, что вам нужно, это немного магии Win32.

Каждый элемент управления имеет «дескриптор», который идентифицирует его, так что операционная система может отправлять ему сообщения. Этот дескриптор предоставляется через свойство Handle каждого элемента управления. Базовая система Win32 фактически позволяет вам прослушивать любой элемент управления WndProc, пока у вас есть его дескриптор. Это означает, что вам не нужно наследовать от элемента управления Winforms, чтобы изменить его поведение Win32. System.Windows.Forms.NativeWindow в .NET оборачивает эту базовую функциональность.

Вот пример того, как вы можете сделать это:

class SmoothScrollIntercept : System.Windows.Forms.NativeWindow
{
    public SmoothScrollIntercept(IntPtr hWnd)
    {
        // assign the handle and listen to this control's WndProc
        this.AssignHandle(hWnd);
    }

    protected override void WndProc(ref Message m)
    {
        // listen to WndProc here, do things

        if ((m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL)
            && (((int)m.WParam & 0xFFFF) == 5)) 
        {
            m.WParam = (IntPtr)(((int)m.WParam & ~0xFFFF) | 4);
        }

        base.WndProc(ref m);
    } 
}

Затем в коде позади прикрепите перехват к элементу управления:

SmoothScrollIntercept intercept = new SmoothScrollIntercept(myControl.Handle);

// myControl is now using smooth scrolling, without inheriting from the control
2 голосов
/ 14 августа 2010

Нет, то, что вы просите, невозможно.Вам придется создавать подклассы, как вы делали это раньше.

Даже если бы это было событие, вы бы не смогли сделать то, что вам нужно.Открытый интерфейс для события только выставляет add и remove;нет никакого способа получить или назначить фактических делегатов, прикрепленных к событию.

Однако, глядя на проблему с другой перспективы, вы можетеиспользуйте интерфейс IMessageFilter для достижения конечного результата, который вы ищете.

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

Послепри повторном просмотре вашего кода IMessageFilter не будет работать, поскольку вы не можете изменить сообщение в PreFilterMessage;Вы можете только исследовать или подавить это.На данный момент лучше всего переопределить WndProc в родительском Form и попытаться провести там свои манипуляции.Не похоже, что есть общее решение вашей проблемы.

...