ich empfehle dir einen Footer Bereich zu zeichnen und dort dein Ergebniss einzutragen.
Hierzu erstelle eine neue Klasse, leite von DataGridView ab und überschreibe das Event z.B. wie folgt.
protected override void OnRowPrePaint(DataGridViewRowPrePaintEventArgs e)
{
if (!e.IsLastVisibleRow)
{
base.OnRowPrePaint(e);
}
else
{
Rectangle rowBounds = new Rectangle(
this.RowHeadersWidth, e.RowBounds.Top,
this.Columns.GetColumnsWidth(
DataGridViewElementStates.Visible) -
this.HorizontalScrollingOffset + 1,
e.RowBounds.Height);
// Paint the custom selection background.
using (Brush backbrush = new System.Drawing.Drawing2D.LinearGradientBrush(rowBounds,
this.DefaultCellStyle.SelectionBackColor,
e.InheritedRowStyle.ForeColor,
System.Drawing.Drawing2D.LinearGradientMode.ForwardDiagonal))
{
e.Graphics.FillRectangle(backbrush, rowBounds);
e.Graphics.DrawString("Das Ergebniss", this.Font, new SolidBrush(Color.Red), rowBounds.Location);
}
e.Handled = true;
}
}
Natürlich fehlen noch ein ein paar feinheiten aber dieser Weg wäre der einfachste. Um das Ergebniss zu ermitteln wirst du natürlich noch mit ForEach durch alle Rows durchgehen müssen.
Du kannst natürlich auch das RowPrePaint abonnomieren.
Dann kannst du versuchen das Handel des Spiels über GetActiveWindow (Win API) zu holen und es zu übermalen indem du dein Control dann darüber zeichnest :)
Man könnte dem Teilabschnitt im Fremden Fenster den man Transparent zeichnen möchte, wie ein Bild behandeln. Man holt sich quasi von der aktuellen Position des Fensters die darunterliegende Fläche als Bild. Dieses Bild gestaltet man leicht Transparent und malt es in die gewünschte Position des fremden Fensters. Es sollte dabei zu keinen Flackereffekten kommen, allerdings solltest du beim bewegen des fremden Fensters das Bild immer aktualisieren.
2.) Lösungsansatz (schwer):
Hierzu ließ dir als erstes in Ruhe den Artikel über Custom Borders durch.
Der Gedanke dabei wäre es über NativeWindow oder SetWindowLong die Windows Nachrichten der Zeichenroutine für den Hintergrund des fremden Fensters abzufangen. An der Stelle an der die Transparente Fläche sein sollte lässt du den normalen Zeichenvorgang weg und benutzt eine transparente Zeichenroutine. Dabei kann es zu Flackereffekten kommen.
Hierzu wirst du evtl. folgende Windows Nachrichten abfangen müssen:
WM_NCPAINT
WM_NCCALCSIZE // evtl. für die NonClient Größe
WM_PRINTCLIENT
WM_NCPAINT wirst du evtl. brauchen da es sein kann das dieser Bereich Schwarz oder Weiß gemalt wird und diesen erstmal allgemein transparent ersetzten musst. WM_NCCALCSIZE kann sein das diese Nachricht nicht benötigst, denke dabei nur an die Ermittlung der NonClient Area Größe.
WM_PRINTCLIENT wirst du benötigen um die Zeichenroutine selbst in die Hand zu nehmen, so das an der gewünschten Position transparent gemalt wird.
Denk also dran, WM_PRINTCLIENT alleine wird nicht ausreichen da sonst dein Transparenter Bereich evtl Schwarz oder weiß gemalt wird.
Du wirst da etwas rumprobieren müssen, es sei jemand findet einen besseren Ansatz.
Viel Glück :)
Nach etwas herum probieren habe ich eine Lösung gefunden.
Allerdings mich dagegen entschieden diese vorerst zu realisieren da der Aufwand enorm ist.
Man kann von der Command Bar das Handle besorgen und muss die WindowsNachrichten abfangen. Dann schiebt man dieser wiederum Controls unter, diese CustomControls müssen wiederum bei CreateParams überschrieben werden und deren Styles werden zum Beispiel (je nach Control) wie folgt gesetzt:
Wichtig ist das man sich dann auch die Koordinaten merkt, sprich man nutzt die Technik eines Controls aber setzt dieses auf das CommandBar wie ein grafisches Object (allerdings mit den Annehmlichkeiten die einen das Control schon bietet).
Für den Button müsste man also eine neue Klasse erstellen und von Button ableiten, wiederum eine neue Klasse erstellen von ComboBox ableiten und eine Klasse erstellen in der man von z.B. UserControl ableitet. In den jeweiligen Klassen überschreibt man je nachdem CreateParams.
Um UserControl werden dann die ComboBox abgeleitet wie die von Button abgeleitete Kalsse eingebunden und das USerControl mit Funktionen versehen.
Wer nun AddIn Express nutzt muss versuchen eine Klasse zu erstellen und von AddinExpress.MSO.ADXCommandBarControl ableiten. Und wiederum beim erstellen des ComObjects versuchen das Microsoft.Core.ICommandControl interface anzubinden. Dann wiederum die location mit der des UserControls tauschen, das geht wenn man die CommandBar ranzieht. Entweder über die outlookApplication oder CommandBar direkt und via NativeWindow die WndProc überschreibt und hier alles mit dem UserControl tauscht…
Man sieht schon, das artet in viel Arbeit aus.
Ein anderer Ansatz wäre die CommandBar nachzubilden was wiederum sehr schwer wird, ein Ansatz liefert dieser Link - hier muss aber bei der CommandBar.Show Methode wiederum etwas nachgebessert werden über einen Hook.
Ein anderer Ansatz:
Eine andere Möglichkeit das Problem zu Lösen wäre ein UserControl zu erstellen dieses via SetWindowLong oder bei CreateParams zu bearbeiten das dieses seine Ränder wie Ansichten Elemente verliert und dieses auf die COmmandBAr zu implementieren. Das ganze wäre etwas weniger Zeitaufwändig, amcht die Sache den noch nicht leichter.
Früher einmal war es unbestimmt - es gab kein Anfang des Tages und kein Ende. Mal waren es nur 30 Stunden mal über ~100 Stunden und ab und an habe ich das Schlafen komplett aufgegeben.
Heute sind es genau 40 Stunden, nicht mehr und nicht weniger und ich bin verdammt froh darüber.
Rauche ebenso recht viel bzw. zu viel.
Trinken tue ich unter der Woche nichts und am Wochenende je nachdem.
Sport übe ich nur je nachdem aus, gibt Tage da Jogge ich sehr gerne oder spiele Volleyball und wiederum Phasen an denen ich nichts tue.
Hallo Community,
ich arbeite zurzeit an einem MS Outlook AddIn und benötige dazu einen Button Ähnlich dem Senden und Empfangen Button von MS Outlook.
Folgende Ansätze habe ich bisher verfolgt:
Nachbilden:
Ich habe zuerst vermutet das der Button aus zwei Grafischen Objekten besteht die über die Schnittstelle CommandBarPopup und CommandBarButton realisiert werden. Dazu habe ich dann anhand der Windowsnachrichten auf der CommandBar versucht via CommandBar.accHitTest die jeweiligen Controls ausfindig zu machen. Soweit hat das ganze auch gut geklappt. Das Problem besteht nun darin das ich die Selection von beiden CommandBarControl’s nicht hinbekomme. Ich dachte wenn ich jeweils auf CommandBarPopup.accSelect und CommandBarButton.accSelect aufrufe könnte ich beide gleichzeitig markieren. Die Schnittstelle CommandBarButton bietet mir die Eigenschaft CommandBarButton.State an. Welche es mir erlaubt den MsoButtonState.msoButtonMixed zu stellen. Hierbei wäre dann der Button zwar Selectiert aber CommandBarPopup bietet diese Eigenscahft nicht an und somit komme ich an dieser Stelle auch nicht weiter. Vielleicht gibt es ja eine der in der Schnittstelle bereitgestellen Methoden die das erlaubt – leider finde ich hierzu keine Dokumentationen wie auf der MSDN Seite, diese sind leer.
// Eigene native windows nachricht für die commandbar auf dem die CommandControl's gezeichnet werden
private void OnMouseMove(object sender, ADXCommandBarMultiBoxBar.CommandBarNativeMouseEventArgs e)
{
if (_ADXOlExplorerCommandBar.CommandBarObj is CommandBar)
{
CommandBar bar = _ADXOlExplorerCommandBar.CommandBarObj as CommandBar;
if (_AdvPop.ControlObj is CommandBarPopup && _AdvBtn.ControlObj is CommandBarButton)
{
CommandBarPopup Pop = _AdvPop.ControlObj as CommandBarPopup;
CommandBarButton Btn = _AdvBtn.ControlObj as CommandBarButton;
CommandBarControl contr = bar.accHitTest(e.MouseEvent.X, e.MouseEvent.Y) as CommandBarControl;
// Tag besitzt als string eine GUID, das object selber ist kein indiez
if (contr != null && (contr.Tag == Pop.Tag || contr.Tag == Btn.Tag))
{
if (!this.IsSelected)
{
this.IsSelected = true;
Pop.accSelect(1, Type.Missing); // selectieren
Btn.accSelect(1, Type.Missing); // selectieren
Btn.State = MsoButtonState.msoButtonMixed
}
}
else
this.IsSelected = false;
}
}
}
Nachbauen:
In einem andere versuch habe ich über die Schnittstelle CommandBarControl auf der Eigenschaft CommandBarControl.Control ein UserControl hinzugefügt. Das Ergebniss funktioniert zwar da ich nun einen VS Button verwenden kann und diese dem Senden und Empfangen Button nachbilde. Aber die Sache mit dem UI ist eher bescheidener Natur und sieht nicht wirklich toll aus.
Mir ist einfach nicht ganz klar ob der Senden und Empfangen Button etwas eigenes von MS ist oder ob es eine Schnittstelle gibt mit der ich diesen Button in die CommandBar nach meinen bedürfnissen einbinden kann.
Vielleicht hat ja jemand eine Idee wie man diesne Button nachbilden könnte.
Leider ist mir der Fehler wegen der Farbe nicht aufgefallen dennoch lässt dieser sich umgehen.
Ändere hierzu die Farbe über den Designer anhand des UI Tool's (siehe Bild). Anschließend zerre etwas an der Größe des Fensters herum und schon wird sich das Layout gemerkt.
Ich habe diese Komponente noch nicht komplett sauber integriert, aber ich hoffe das es ca. den Weg aufzeigt wie man eigene Styles auf Controls anwenden kann. Hierzu gibt es im Forum noch andere interessante Ansätze :-)
hier ein kleines Codesnipped für Add-In Express ADXOlExplorerCommandBar.
Folgendes Problem soll damit behoben werden, ADXCommandBarControl's bieten zum Teil nur Events an wie z.B. beim ADXCommandBarButton nur der Klick oder beim ADXCommandBarPopup sogar gar keine Events an.
Um dieses Problem zu beheben, habe ich eine kleine Komponente geschrieben.
Evtl. kann diese noch jemand gebrauchen. Die Komponente kann auch beliebig dann noch durch hinzufügen bzw. abfangen der Windowsnachrichten erweitert werden.
Hiwneis:
- Es wird AddinExpress dafür benötigt! Die Events können über den Designer herkömmlich über das Event Symbol via doppelklick automatisch hinzugefügt werden, dazu einfach die Komponente auf den Designer ziehen.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using AddinExpress.MSO;
using Office;
using System.Windows.Forms;
using IApplication = Outlook.Application;
using IExplorer = Outlook.Explorer;
namespace CobraOlAddIn12
{
[ComVisible(false)]
[DesignTimeVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ToolboxBitmap(typeof(Button))]
[ToolboxItem(true)]
public class ADXOlExplorerCommandBarEvents : ADXBaseAppEvents, ISupportInitialize
{
#region Declarations
/// <summary>
///
/// </summary>
private class CommandBarNative : NativeWindow
{
#region Imports
[DllImport("User32.dll")]
private static extern IntPtr FindWindow(string strClassName, string strWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
private static extern bool GetWindowInfo(IntPtr hwnd, ref WINDOWINFO pwi);
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x202,
WM_MBUTTONDOWN = 0x207,
WM_MBUTTONUP = 0x208,
WM_RBUTTONDOWN = 0x204,
WM_RBUTTONUP = 0x205,
WM_MOUSEMOVE = 0x200,
WM_MOUSEWHEEL = 0x20A,
WM_MOUSEHWHEEL = 0x20E,
}
private static readonly IntPtr TRUE = new IntPtr(1);
private static readonly IntPtr FALSE = IntPtr.Zero;
[StructLayoutAttribute(LayoutKind.Sequential)]
private struct WINDOWINFO
{
/// DWORD->unsigned int
public uint cbSize;
/// RECT->tagRECT
public Rectangle rcWindow;
/// RECT->tagRECT
public Rectangle rcClient;
/// DWORD->unsigned int
public uint dwStyle;
/// DWORD->unsigned int
public uint dwExStyle;
/// DWORD->unsigned int
public uint dwWindowStatus;
/// UINT->unsigned int
public uint cxWindowBorders;
/// UINT->unsigned int
public uint cyWindowBorders;
/// ATOM->WORD->unsigned short
public ushort atomWindowType;
/// WORD->unsigned short
public ushort wCreatorVersion;
}
#endregion
#region Declarations
public event EventHandler<CommandBarNativeMouseEventArgs> MouseUpEvent;
public event EventHandler<CommandBarNativeMouseEventArgs> MouseDownEvent;
public event EventHandler<CommandBarNativeMouseEventArgs> MouseMoveEvent;
#endregion
#region Properties
/// <summary>
///
/// </summary>
public ADXOlExplorerCommandBar CommandBar { get; private set; }
/// <summary>
///
/// </summary>
public IntPtr ObjHandle
{
get
{
if (this.CommandBar == null) return IntPtr.Zero;
CommandBar cmdBar = this.CommandBar.CommandBarObj as CommandBar;
if (cmdBar == null) return IntPtr.Zero;
IApplication app = cmdBar.Application as IApplication;
if (app == null) return IntPtr.Zero;
IExplorer ex = app.Application.ActiveExplorer() as IExplorer;
IntPtr hWnd = FindWindow("rctrl_renwnd32", ex.Caption);
IntPtr hDocktop = FindWindowEx(hWnd, IntPtr.Zero, "MsoCommandBarDock", "MsoDockTop");
IntPtr hCmdBar = FindWindowEx(hDocktop, IntPtr.Zero, "MsoCommandBar", cmdBar.Name);
return hCmdBar;
}
}
#endregion
#region Constructors
/// <summary>
///
/// </summary>
/// <param name="cmdBar"></param>
public CommandBarNative(ADXOlExplorerCommandBar cmdBar)
{
this.CommandBar = cmdBar;
}
#endregion
#region Methods
/// <summary>
///
/// </summary>
/// <param name="m"></param>
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case (int)MouseMessages.WM_MBUTTONDOWN:
case (int)MouseMessages.WM_RBUTTONDOWN:
case (int)MouseMessages.WM_LBUTTONDOWN:
{
this.WmMBDown(ref m);
if (m.Result == TRUE) return;
break;
}
case (int)MouseMessages.WM_MBUTTONUP:
case (int)MouseMessages.WM_RBUTTONUP:
case (int)MouseMessages.WM_LBUTTONUP:
{
this.WmMBUp(ref m);
if (m.Result == TRUE) return;
break;
}
case (int)MouseMessages.WM_MOUSEMOVE:
{
this.WmMBMove(ref m);
if (m.Result == TRUE) return;
break;
}
}
base.WndProc(ref m);
}
/// <summary>
///
/// </summary>
/// <param name="m"></param>
private void WmMBMove(ref Message m)
{
CommandBarNativeMouseEventArgs args = new CommandBarNativeMouseEventArgs();
args.MouseEvent = this.CreateMouseEventArgs(m.LParam, m.Msg);
if (this.MouseMoveEvent != null)
this.MouseMoveEvent.Invoke(this, args);
}
/// <summary>
///
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
private MouseButtons GetMouseButton(int msg)
{
switch (msg)
{
case (int)MouseMessages.WM_LBUTTONUP:
case (int)MouseMessages.WM_LBUTTONDOWN:
return MouseButtons.Left;
case (int)MouseMessages.WM_MBUTTONUP:
case (int)MouseMessages.WM_MBUTTONDOWN:
return MouseButtons.Middle;
case (int)MouseMessages.WM_RBUTTONUP:
case (int)MouseMessages.WM_RBUTTONDOWN:
return MouseButtons.Right;
}
return MouseButtons.None;
}
/// <summary>
///
/// </summary>
/// <param name="m"></param>
private void WmMBDown(ref Message m)
{
CommandBarNativeMouseEventArgs args = new CommandBarNativeMouseEventArgs();
args.MouseEvent = this.CreateMouseEventArgs(m.LParam, m.Msg);
if (this.MouseDownEvent != null)
this.MouseDownEvent.Invoke(this, args);
m.Result = (args.Cancel) ? TRUE : FALSE;
}
/// <summary>
///
/// </summary>
/// <param name="m"></param>
private void WmMBUp(ref Message m)
{
CommandBarNativeMouseEventArgs args = new CommandBarNativeMouseEventArgs();
args.MouseEvent = this.CreateMouseEventArgs(m.LParam, m.Msg);
if (this.MouseUpEvent != null)
this.MouseUpEvent.Invoke(this, args);
m.Result = (args.Cancel) ? TRUE : FALSE;
}
/// <summary>
///
/// </summary>
/// <param name="lParam"></param>
/// <param name="m"></param>
/// <returns></returns>
private MouseEventArgs CreateMouseEventArgs(IntPtr lParam, int m)
{
Point pt = new Point(lParam.ToInt32());
Point pt2 = this.PointToCmd(pt);
return new MouseEventArgs(this.GetMouseButton(m), 0,
pt2.X, pt2.Y, 0);
}
/// <summary>
///
/// </summary>
/// <param name="screenPoint"></param>
/// <returns></returns>
private Point PointToCmd(Point screenPoint)
{
WINDOWINFO windowInfo = new WINDOWINFO();
GetWindowInfo(base.Handle, ref windowInfo);
return new Point(windowInfo.rcClient.Location.X + screenPoint.X, windowInfo.rcClient.Location.Y + screenPoint.Y);
}
#endregion
}
/// <summary>
///
/// </summary>
private class CommandBarNativeMouseEventArgs : CancelEventArgs
{
public MouseEventArgs MouseEvent { get; set; }
}
#endregion
#region Properties
/// <summary>
///
/// </summary>
private Dictionary<ADXOlExplorerCommandBar, CommandBarNative> CommandBars { get; set; }
/// <summary>
///
/// </summary>
public override string ClassName
{
get
{
this.Bind();
return this.GetType().ToString();
}
}
#endregion
#region Events
[Category("Explorer")]
[Description("Occurs before command bars events from the active explorer.")]
public event EventHandler<CommandBarControlMouseEventArgs> MouseDown;
[Category("Explorer")]
[Description("Occurs before command bars events from the active explorer.")]
public event EventHandler<CommandBarControlMouseEventArgs> MouseUp;
[Category("Explorer")]
[Description("Occurs before command bars events from the active explorer.")]
public event EventHandler<CommandBarControlMouseEventArgs> MouseMove;
#endregion
#region Constructors
/// <summary>
///
/// </summary>
public ADXOlExplorerCommandBarEvents()
: base()
{
this.CommandBars = new Dictionary<ADXOlExplorerCommandBar, CommandBarNative>();
}
/// <summary>
///
/// </summary>
/// <param name="container"></param>
public ADXOlExplorerCommandBarEvents(IContainer container)
: base(container)
{
this.CommandBars = new Dictionary<ADXOlExplorerCommandBar, CommandBarNative>();
container.Add(this);
}
#endregion
#region Methods
/// <summary>
///
/// </summary>
public void Bind()
{
foreach (ADXOlExplorerCommandBar bar in CommandBars.Keys)
{
CommandBars[bar].ReleaseHandle();
CommandBars[bar].AssignHandle(CommandBars[bar].ObjHandle);
}
}
/// <summary>
///
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose(bool disposing)
{
// release handles (stop wndproc/hook)
foreach (ADXOlExplorerCommandBar bar in CommandBars.Keys)
CommandBars[bar].ReleaseHandle();
base.Dispose(disposing);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnMouseUpEvent(object sender, CommandBarNativeMouseEventArgs e)
{
if (sender is CommandBarNative)
{
CommandBarNative native = sender as CommandBarNative;
foreach (ADXCommandBarControl cmdCtr in native.CommandBar.Controls)
{
CommandBarControl control = cmdCtr.ControlObj as CommandBarControl;
if (control.accHitTest(e.MouseEvent.X, e.MouseEvent.Y) != null)
{
CommandBarControlMouseEventArgs ctrEvnt = new CommandBarControlMouseEventArgs(
e.Cancel,
e.MouseEvent.Button,
e.MouseEvent.Location);
if (this.MouseUp != null) this.MouseUp(cmdCtr, ctrEvnt);
e.Cancel = ctrEvnt.Cancel;
}
}
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnMouseDownEvent(object sender, CommandBarNativeMouseEventArgs e)
{
if (sender is CommandBarNative)
{
CommandBarNative native = sender as CommandBarNative;
foreach (ADXCommandBarControl cmdCtr in native.CommandBar.Controls)
{
CommandBarControl control = cmdCtr.ControlObj as CommandBarControl;
if (control.accHitTest(e.MouseEvent.X, e.MouseEvent.Y) != null)
{
CommandBarControlMouseEventArgs ctrEvnt = new CommandBarControlMouseEventArgs(
e.Cancel,
e.MouseEvent.Button,
e.MouseEvent.Location);
if (this.MouseDown != null) this.MouseDown(cmdCtr, ctrEvnt);
e.Cancel = ctrEvnt.Cancel;
}
}
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnMouseMoveEvent(object sender, ADXOlExplorerCommandBarEvents.CommandBarNativeMouseEventArgs e)
{
if (sender is CommandBarNative)
{
CommandBarNative native = sender as CommandBarNative;
foreach (ADXCommandBarControl cmdCtr in native.CommandBar.Controls)
{
CommandBarControl control = cmdCtr.ControlObj as CommandBarControl;
if (control.accHitTest(e.MouseEvent.X, e.MouseEvent.Y) != null)
{
CommandBarControlMouseEventArgs ctrEvnt = new CommandBarControlMouseEventArgs(
e.Cancel,
e.MouseEvent.Button,
e.MouseEvent.Location);
if (this.MouseMove != null) this.MouseMove(cmdCtr, ctrEvnt);
e.Cancel = ctrEvnt.Cancel;
}
}
}
}
/// <summary>
///
/// </summary>
public void BeginInit() { }
/// <summary>
///
/// </summary>
public void EndInit()
{
foreach (IComponent component in base.Container.Components)
{
if (component is ADXOlExplorerCommandBar)
{
ADXOlExplorerCommandBar bar = component as ADXOlExplorerCommandBar;
CommandBarNative native = new CommandBarNative(bar);
native.MouseDownEvent += new EventHandler<CommandBarNativeMouseEventArgs>(this.OnMouseDownEvent);
native.MouseUpEvent += new EventHandler<CommandBarNativeMouseEventArgs>(this.OnMouseUpEvent);
native.MouseMoveEvent += new EventHandler<CommandBarNativeMouseEventArgs>(this.OnMouseMoveEvent);
CommandBars.Add(bar, native);
}
}
}
#endregion
}
public class CommandBarControlMouseEventArgs : CancelEventArgs
{
#region Properties
/// <summary>
// Summary:
/// Gets which mouse button was pressed.
///
/// Returns:
/// One of the System.Windows.Forms.MouseButtons values.
/// </summary>
public MouseButtons Button { get; private set; }
/// <summary>
/// Summary:
/// Gets the location of the mouse during the generating mouse event.
///
/// Returns:
/// A System.Drawing.Point containing the x- and y- coordinate of the mouse,
/// in pixels.
/// </summary>
public Point Location { get; private set; }
/// <summary>
// Summary:
/// Gets the x-coordinate of the mouse during the generating mouse event.
///
/// Returns:
/// The x-coordinate of the mouse, in pixels.
/// </summary>
public int X { get; private set; }
/// <summary>
/// Summary:
/// Gets the y-coordinate of the mouse during the generating mouse event.
///
/// Returns:
/// The y-coordinate of the mouse, in pixels.
/// </summary>
public int Y { get; private set; }
#endregion
#region Constructors
/// <summary>
///
/// </summary>
/// <param name="cancel"></param>
public CommandBarControlMouseEventArgs(bool cancel, MouseButtons btn, Point location)
{
this.Cancel = cancel;
this.Button = btn;
this.Location = location;
this.X = location.X;
this.Y = location.Y;
}
#endregion
}
}
Hinweis:
Das snipped kann noch verschönert werden, aber ist im derzeitigen Zustand Funktionsfähig. Man kann den Weg den ich gegangen bin auch verwenden und ein Control ADXCommandPopup (Custom) zu erstellen und z.B. auch Bilder zusätzlich vom Menüpunkt hinzuzufügen - was standardmäßig ja nicht geht. Hierzu einfach den Weg über WindowsNative gehen ähnlich wie bei diesem Snipped.
Anwendungsbeispiel:
Clickevent für z.B. ADXCommandBarPopup mit verhindern des aufklappen der
Menüleiste.
Erst nach Dialogabfrage == true wird dann ein aufklappen der Menüleiste möglich.
/Ps
Habe es nicht unter Snipped gestellt da es eher um Office Anwendungen geht. Falls es doch an der falschen Stelle sein sollte, entschuldige ich mich jetzt schon mal dafür.
Ich vermute mal du hast die Windows Vista 64 Bit Version - genau nachgelesen habe ich es auch nie, aber ich glaube bei Win32 wird expliziet die Kernel32.dll von Windows 32 Bit verwendet die vermutlich auch bei Windwos Vista 64 Bit vorliegt.
Hrm, fürchte da musst einfach mal im Internet recherchieren.
Man kann es aber auch noch anders Lösen: Link. Nur dann geht es eben nur auf den Windows 64 Bit Versionen.
Hrm, alternativ kann man auch Beginn und EndPaint verwenden allerdings nur in verbindung mit der Windows API und überschreiben der WM_PAINT um diese dann wieder auf das OnPaint des Controls zu linken.
Unter dem Artikel: Artikel] Custom Window Border für Form's habe ich auch noch eine Lösung gegen Flackereffekte geschrieben, diese würde ebenso funktionieren. Sobald CreateParams und somit auch der WindowsStyle sich ändert, kommt es manchmal zu Problem beim Zeichnen.
Hrm, wie setzt du deine Forms so das sie als Owner das CRM (Haupt / MDIParent ) Form haben? - Das ist entscheidend für das Verhallten auch der der Dialoge.
Machst du das via IWin32Form oder per Win API über SetParent?
Hat das CRM Programm einen Formulardesigner (sowas wie list and Labels)?
Benutzt das CRM Programm DevExpress und du auch?
Soll die Sprache über die Installationsroutine getriggert werden?
normal reicht es wenn man alles in die Resourcen auslagert, egal ob *.dll (Plugin) oder *.Exe Datei. Die jeweiligen Resourcen werden dann in die verschiedenen Sprachen übersetzt. Hierfür eignet sich zum Beispiel das kostenpflichtige Programm PASSOLO.
Über Installschild kann man dann die Resourcen ersetzten lassen indem man bei der Installationsroutine eine Sprachauswahl einbindet. Oder noch besser, man überlässt die Art von Drecksarbeit einem Tool wie z.B. dem FinalBuilder.
Denk dran, bei Forms kannst Localizeable = True einstellen und der PASSOLO Mensch kann diese resourcen auch ändern.
Rate dir bzw. deiner Firma eh zu diesen beiden Tools, den ohne automatischen Buildprozess und geschickter Übersetzung verschwendet man dafür viel zu viel Zeit.
Soll die Sprache dynamisch zur Laufzeit geändert werden können?
Hierzu einfach wieder den Weg über die Resourcen gehen und diese einfach über ein Interface validieren lassen, das ganze sähe dann wie es kleines_eichhoernchen vorgeschlagen hat aus.
Da du nicht mit MDI-Parent / MDI-Children arbeitest ist es mir nicht genau verständlich was du mit MainForm und anderen Fenstern genau meinst. Beinhaltet dein Hauptformular über MainForm.Controls.Add(Form1) ein Formular usw?
Ich denke am besten wäre ein kleines Bild wie man sich das ganze vorzustellen hat.
Irgendwie habe ich das befürchtet - daher wäre das eleganteste wohl ein Invalidate() auf das USerControl oder die Form aufzurufen wenn sich der MDIRoot ändert, im Prinzip amchst du das ja schon.
Der Fehler liegt aber nicht bei dir, hatte da auch mal was dunkel in Erinnerung das es beim Maximieren, Minimieren und vergrößern von MDIContainer schwierigkeiten bei der WM_PAINT Windowsnachricht gab. Leider weis ich nicht mehr wie ich das gelöst habe, irgendwas elegantes viel mir da schon ein.Vielleicht erinnere ich mich wieder nach ner Flasche Rum :-)
Ansonsten, bleib mal bei deiner Lösung, die klingt nichtmal so schlecht in diesem Fall.
eine Sache habe ich nicht ganz verstanden, der boolische Wert der als Übergabeparameter an deine Emthode "RUN" gesendet wird ist mir nicht ganz klar. Ich vermute mal, das bei "true" die entsprechende *.DLL (Plugin) dann laden soll. Und beziehe daher auch den Lösungsansatz auf diese Vermutung:
Ein Weg wäre es die Informationen als Serialisierte Klasse zur verfügung zu stellen und wie folgt würde das ganze aussehen:
Zuerst erstellst du eine Klasse namens Serializer<T> diese generische Klasse dient als Standardserializer und ist eine reine Hilfsklasse. Innerhalb dieser Klasse erstellst du ein Proeprtie namens Path vom typ string, dann erstellst du zwei statsiche Methoden die wie folgt aussehen könnten:
static internal void Save(string fileName, T obj)
{
this.FileName = fileName;
try
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (TextWriter writer = new StreamWriter(fileName))
{
serializer.Serialize(writer, obj);
writer.Flush();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.Fail("Fehler beim Speichern der " + fileName + ex.Message);
throw;
}
}
static internal T Load(string fileName)
{
try
{
T obj = null;
using (TextReader reader = new StreamReader(fileName))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
obj = serializer.Deserialize(reader);
}
return obj;
}
catch (Exception ex)
{
System.Diagnostics.Debug.Fail("Fehler beim Laden der " + fileName + ex.Message);
}
return null;
}
Ebenso erstellst du noch zwei weitere public methoden, Load und Save, die ähnlich der Statischen Methoden aufgebaut sind, du musst legendlich den generischen typen dazu weglassen und auf this.GetType() umstellen.
Danach erstellst du eine Klasse Namens PluginInfo, diese besitzt als public Properties die entsprechenden Namen äquivalent zu den <NodeTags> deiner XML. Deine XML sollte folgende "wichtigen" Tags besitzen und somit deine Kalsse auch Properties:
string ClassName // klasse der Interfaceschnittstelle
string Location // Ort der Assembly (GAC/ABSOLUT)
string AssemblyName // Absoluter Pfad + <Assembly>.dll oder GAC
Der Klasse PluginInfo verpasst du dann das Attribut [Serializable()] diese erhällst du aus den Namespace von System.Xml.Serialization. und leitest PluginInfo von Serializer<T> ab, also PluginInfo : Serializer<PluginInfo>.
Im Anschluss erstellst du weiderum eine Klasse Namens AssemblyActivator, die wird als generischer Typ für das aktivierne von Assemblys verwendet und zeitgleich in das zu verwendende Interface gecastet. Du solltest grundsätzlich immer Factory für Plugins verwenden genauso wie Interfaces.
Diese stelle ich dir mal ganz rein, da es sonst zu komplex ist das niederzuschreiben:
public enum AssemblyLocation
{
Gac, // Gloabl Assembly Cache
AbsolutePath // Der Pfad ist in AssemblyName komplett angegeben
}
public class AssemblyActivator<T, I> : Helper.Serializer<T>
{
private AssemblyLocation _Location;
public AssemblyLocation Location
{
get
{
return _Location;
}
set
{
_Location = value;
}
}
private string _AssemblyName;
public string AssemblyName
{
get
{
return _AssemblyName;
}
set
{
_AssemblyName = value;
}
}
private string _ClassName;
public string ClassName
{
get
{
return _ClassName;
}
set
{
_ClassName = value;
}
}
internal I CreateObject()
{
Assembly a = null;
string AssemblyPath = (Location == AssemblyLocation.CobraModule) ?
Cobra.APUtil.APModules.ModuleManager.ProgramDirectory + @"Module\" + AssemblyName : AssemblyName;
try
{
if (Location != AssemblyLocation.Gac)
{
a = Assembly.LoadFrom(AssemblyPath);
}
else
{
a = Assembly.Load(AssemblyPath);
}
}
catch (FileNotFoundException e)
{
MessageBox.Show(e.Message);
return default(I);
}
Type ClassType;
try
{
ClassType = a.GetType(ClassName);
}
catch (Exception ex)
{
MessageBox.Show("Class " + ClassName + " not found in Assembly " + AssemblyName + " at " + AssemblyPath + "\r\n\r\n" + ex.Message);
return default(I);
}
if (ClassType == null)
{
MessageBox.Show("Class " + ClassName + " not found in Assembly " + AssemblyName + " at " + AssemblyPath);
return default(I);
}
return (I)Activator.CreateInstance(ClassType);
}
}
}
Deine Methode Run solltest du dann wie folgt erstellen:
public static PluginInfo RUN(string path, bool initalize){};
In der Methode Run rufst du den über den Parameter path nun über PluginInfo.Load(path die Plugininfo ab. Wenn der Parameter initalize = true ist dann holst du dir über den Assemblyactivator und anhand der PluginInfofiles (daher auch selber Namensraum der Porperties) über eine Schnittstelle die du noch erstellen musst (z.B. IPlugin) das Plugin ab und ladest es somit hinein. Der activator kann dann schon automatisch anhand der Konfiguration des XML Files festellen ob die assembly im global assemblycache zu suchen ist oder ob sie irgendwo anders liegt.
Als Rückgabewert gibst dann das PluginInfo zurück, somit kann jeder der von aussen auf deine RUN Methode drauf zugreift gleich die Assembly identifizieren.
Wenn den AssemblyActivator noch mit der PluginInfo Klasse verbindest wäre das noch schöner, das sähe ca. wie folgt aus:
PluginInfo : AssemblyActivator<Serializer<PluginInfo>>,IPlugin>
Damit könnte man dann zugleich in der IPluginInfo auch die Assembly von aussen her reinladen falls sie noch nicht geladen wurde.
Wenn das zu komplex sein sollte siehe Stichwörter:
System.Xml.Serialization.IXmlSerializable
Serialize
/PS
Einen kleinen gefallen Bitte - bennen den Titel des Beitrags noch um in z.B. "[Offen] XML Serialisieren / Assemblys Laden".
- OnPaint beim UserControl wird nicht angesprungen und die damit verbundene die Windows Nachricht WM_PAINT wird nicht an das UserControl gesendet wenn das MDIRoot sich vergrößert / verkleinert.
- Die grafischen Objecte des UserControls richten sich an dem sichtbaren Bereich aus, was sehr wahrscheinlich ist da die Zeichenroutine sich automatisch der größe des sichtbaren Bereichs anpassen. Das passiert wenn man z.B. beim verändern der größe des Controls die Zeitleiste und deren Inhallte vergrößern / verkleinern will.
- Die Form1 sendert WM_PAINT nicht an das UserControl weiter.
@ markus.bodlos
Das alles lässt sich schön mit UserControls statt dann Forms abbilden :-)
Aber hatte ohend as Bild von Echo]6[ auch Probleme zu verstehen inwiefern dann ein MDIContainer da mit einspielt.
@Echo]6[
Für die umsetzung mit UserControls zum Abbild von markus.bodlos, folgende vorgehensweise:
Ich schreibe es mal mit SplitContainer statt TableLayout, da man eigentlich dem Benutezr die Möglichkeit geben sollte die Breite der Ansichten selbst zu bestimmen.
Erstelle einfach eine MainForm, diese MainForm ist "kein MDIContainer", daher IsMdiContainer = false. Als zweites hast du einen SplitContainer der Senkrecht ist und DockStyle = Fill.
In diesen SplitContainer hast du links die Menüs rechts ist dein Content Bereich.
In den Contentbereich machst du wieder einen SplitContainer rein, diese trennt waagerecht. Oben hast du das fixe UserControl unten das dynamische Usercontrol.
So, warum UserControls, deine UserControls sind deine "ChildForms", sprich für jedes Childform was du davor hattest hast du nun ein eigenes UserControl. Damit die UserControls im unteren Bereich dynamsich reingeladen werdne können schreibst du ein Interface das du in alle UserControls implementierst.
Warum show und Hide, ganz einfach, getätigte Einstellungen sollen nicht verloren gehen. Daher initalisierst du deine UserControls und behällst sie in einer Liste von deiner MainForm. Wenn nun ein menüpunkt links gedrückt wird, löst du auf deiner MainForm ein CklickEvent aus, im ClickEvent selber erhällst du wieder als sender das entsprechende Menü, wenn Menü X daher gedrückt lade den Type = ControlTypeA usw.
Das ganze geht sehr schnell zu programmieren, ca. 10-20 min.
keine Angst hier wurde noch niemand gebissen wegen einer Frage :-)
Das was du brauchst ist das Form.Load Event die du auf deinem MDIContainer (Hauptformular, in dem die MDIForms initialisiert werden).
Wie du das genau machst findest du in folgenden Link: [FAQ] Eigenen Event definieren / Information zu Events
Wenn du das Event in deinem MDIContaine abonomiert hast, einfach den menüpunkt auf <Menu>.Enabled = false stellen und das war es. Wenn das Formular wiederum geschlossen wird, das ist dass Closed Event des MDIForms, dann den Menüpunkt auf <Menu>.Enabled = true stellen um diesen wieder verfügbar zu machen.
/PS
Wenn es etwas gehobener sein darf, schau mal im Netz nach CommandPattern -> Actions / ActionPattern. Das ganze basiert ebenso auf Events aber beinhaltet einer absolut saubere Ausführung von Events die beim initalisieren eines MDIForms sich selbst abonomieren.
Wie gesagt, das Ding hab ich in knapp ner Minute erstellt - es ist simpel die Frage ist nur was man möchte. Sollen die Border der MDIControl aktiv sein, darf der Benutzer das MDIControlerform verschieben usw.
Das beste wäre wie gesagt, Echo]6[ malt nen Bild wie es genau auszusehen hat und was der Benutzer darf und was nicht, wie das verschieben.
Pria Weg geht auch, das Problem dabei ist, dass das ChildForm (MDIForm) innerhalb der MainForm (ParentForm) verschoben werden kann vom Benutzer. Die Möglichkeit geht auch, wenn man dem Benutzer die Möglichkeit nimmt das ChildForm zu bewegen.
Das was der Threadersteller eigentlich möchte ist so ne Sache, eigentlich ist das ein Widget Thema, kein MDI. Ein MDI ist ein Container der es expleziet dem Benutzer erlaubt innerhalb des Containers schalten und walten zu können wie es ihm beliebt.
Man kann beide Vorschläge, also deinen und Pria's mixen. Oberhalb des MDIContainers kann man über WinAPI SetParent (evtl. muss man noch ein bisschen mehr tun) das fest platzierte ChildForm anbringen und dann wie gehabt bei den variablen MDIChilds diese im Container mit aufnehmen.
Das Problem an der Geschichte ist mehr das es zu viele Möglichkeiten gibt das ganze zu lösen. Die Frage ist daher eher was man genau möchte. Im Prinzip kann man das ganze auch mit UserControls lösen. Wie gesagt, am besten wäre es Echo]6[ malt nen Bild, hängt es an und sagt, genauso und nicht anders muss es aussehen.
Allerdings denke ich das dien Vorschlag wirklich ausreicht, wenn es einfach sein darf :-)
Hrm, das ganze ist nicht so einfach und leider gibt es viele Lösungsmöglichkeiten:
Schau mal im Forum was du unter Windows Widget so findest, ansonsten kannst dir auch mal gerne die UtilitiesLib von mir ansehen – dort habe ich es zwar etwas komplizierter gemacht aber im Prinzip lässt sich das Vereinfachen:
Erstell eine neue Klasse und leite von Form ab, erstelle einen neue Propertie vom Typ System.Windows.Forms.DockStyle mit setter und getter. Erstelle dann ein Propertie vom Typ Control für das ParentMDIControl.
Überschreibe die WndProc(ref Message m), fange die Windowsnachricht Nachrichten WindowsMessages.WM_NCHITTEST ab. Gebe anhand der Positionierung des DockStyle die entsprechenden HitTest Werte zurück. Ergo, DockStyle.Bottom -> NCHITTEST für Klick auf den linken, links unten, rechts, rechts unten, rechts oben, links oben und unteren Rand = HITTEST.HTCLIENT während bei klick auf oberen Rand = HITTEST. HTTOP. Somit unterbindest du das verschieben des Fensters.
Fange dann die Windowsnachricht WM_CREATE ab, klinke dabei über SetParent anhand des ParentMDIControl.Handel das this.Handle ein.
Soo wenn nun den Designer öffnest und das ChildForm einbindest wird es zur Laufzeit in den Designer geladen. Der Vorteil, du kannst auf deinem MainForm direkt das ChildForm bearbeiten und irgendwelche Controls draufziehen.
Kannst dir auch mal das Widget Projekt in der UtilitiesLib von mir ansehen, dann erkennst evtl. den Weg den man verwenden kann. Ich nutze dabei nur ein Panel das ich etwas modifiziere so das es wieder ein Windows Form wird. Den schlussendlich sind alle Controls wieder als Form herstellbar, kannst also auch aus einer TreeList wieder ein Form machen.
Wenn der Weg zu komplex sein sollte, gibt es auch eine reine .NET Lösung (ist eher dann ein gebastel) oder Fremdanbieter Controls (die aber kosten).
Wie definierst du "konkreten Typ" und dessen Untauglichkeit für Plug-In Implementierung?
Ein konkreten Typ wäre z.B. eine Klasse statt eines Interfaces.
Ich denke er meinte das es gut wäre vieles über Schnittstellen also Interfaces und intern so viel wie Möglich über Factory abzubilden, damit unabhängig von Typen bleibst.