.NET custom form designer: How to implement IMenuCommandService? - c#

I've got a report form designer written long ago for a database project. It used a lot of winapi magic hence i was forced to rewrite some parts 'in proper way'.
Thanks to some articles from MSDN magazine (here and here) and CodeProject i was able to implement designer surface, toolbox and undo/redo engine.
Every resource i discovered on the topic so far is a bit outdated. Can you point to fresh/comprehensive article?
Code from article mentioned above seems not working.
MenuCommandService.ShowContextMenu is called but nothing appears since there aren't any DesignerVerbs in globalVerbs collection. Should i add 'standard' ones, corresponded to designer actions such as cut/paste, manually? If yes, how can i accomplish this?

Thanks to SharpDevelop sources i was able to figure out the solution
This minimal implementation (some standart commands, no more) worked for me
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing;
namespace DesignerHost
{
class MenuCommandServiceImpl : MenuCommandService
{
DesignerVerbCollection m_globalVerbs = new DesignerVerbCollection();
public MenuCommandServiceImpl(IServiceProvider serviceProvider)
: base(serviceProvider)
{
m_globalVerbs.Add(StandartVerb("Cut", StandardCommands.Cut));
m_globalVerbs.Add(StandartVerb("Copy", StandardCommands.Copy));
m_globalVerbs.Add(StandartVerb("Paste", StandardCommands.Paste));
m_globalVerbs.Add(StandartVerb("Delete", StandardCommands.Delete));
m_globalVerbs.Add(StandartVerb("Select All", StandardCommands.SelectAll));
}
private DesignerVerb StandartVerb(string text, CommandID commandID)
{
return new DesignerVerb(text,
delegate(object o, EventArgs e)
{
IMenuCommandService ms =
GetService(typeof(IMenuCommandService)) as IMenuCommandService;
Debug.Assert(ms != null);
ms.GlobalInvoke(commandID);
}
);
}
class MenuItem : ToolStripMenuItem
{
DesignerVerb verb;
public MenuItem(DesignerVerb verb)
: base(verb.Text)
{
Enabled = verb.Enabled;
this.verb = verb;
Click += InvokeCommand;
}
void InvokeCommand(object sender, EventArgs e)
{
try
{
verb.Invoke();
}
catch (Exception ex)
{
Trace.Write("MenuCommandServiceImpl: " + ex.ToString());
}
}
}
private ToolStripItem[] BuildMenuItems()
{
List<ToolStripItem> items = new List<ToolStripItem>();
foreach (DesignerVerb verb in m_globalVerbs)
{
items.Add(new MenuItem(verb));
}
return items.ToArray();
}
#region IMenuCommandService Members
/// This is called whenever the user right-clicks on a designer.
public override void ShowContextMenu(CommandID menuID, int x, int y)
{
// Display our ContextMenu! Note that the coordinate parameters to this method
// are in screen coordinates, so we've got to translate them into client coordinates.
ContextMenuStrip cm = new ContextMenuStrip();
cm.Items.AddRange(BuildMenuItems());
ISelectionService ss = GetService(typeof (ISelectionService)) as ISelectionService;
Debug.Assert(ss != null);
Control ps = ss.PrimarySelection as Control;
Debug.Assert(ps != null);
Point s = ps.PointToScreen(new Point(0, 0));
cm.Show(ps, new Point(x - s.X, y - s.Y));
}
#endregion
}
}
Update
found similar solution

I ran into same problem a month ago and got something really intersting and helpful from CodePlex here. I read about this on Brad Abram's Blog Post. These includes many examples on framework extensibility and Custom Windows Form Designer Interface is one of them.
Link Txt 1: http://mef.codeplex.com/
Link Txt 2: http://blogs.msdn.com/brada/archive/2009/04/13/managed-extensibility-framework-preview-5-released.aspx
Thanks.

Related

C# VSTO-Powerpoint-TaskPanes in separate windows

I have created a PowerPoint VSTO Addin with a custom Task pane - and a ribbon where a toggle button defines the display / hide Status of the custom Task pane. Basis for this was the Microsoft Walkthrough information for custom Task pane and synchronizing the Ribbon with the Task pane.
So fare everything works fine with the first PowerPoint window. I'm able to show the Task pane in the second and third PowerPoint window, but the toggle button on the ribbon only reacts to the last opened / created PowerPoint window and not to the Task pane displayed / hidded in the active PowerPoint window.
I've found another thread which explains exactly the same Problem here:
C# VSTO-Powerpoint-TaskPanes in separate windows.
But I don't understand the answer neither I don't know how to implement a PowerPoint Inspector Wrapper.
I'm new in C# and just getting a keyword like "Inspector Wrapper" is to less for me. I already spend hours in searching the net but wasn't successfull till now.
Is there a chance to get a COMPLETE code example for PowerPoint how this works, what has to be done?
Code added:
I took the code from the General walkthrough: https://msdn.microsoft.com/en-us/library/bb608590.aspx and changed it with an Event for new presentations:
The code for the ThisAddIn.cs is as follow:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
using Office = Microsoft.Office.Core;
namespace PowerPointAddIn1
{
public partial class ThisAddIn
{
private TaskPaneControl taskPaneControl1;
private Microsoft.Office.Tools.CustomTaskPane taskPaneValue;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.AfterNewPresentation += new Microsoft.Office.Interop.PowerPoint.EApplication_AfterNewPresentationEventHandler(NewPresentation);
//taskPaneControl1 = new TaskPaneControl();
//taskPaneValue = this.CustomTaskPanes.Add( taskPaneControl1, "MyCustomTaskPane");
//taskPaneValue.VisibleChanged += new EventHandler(taskPaneValue_VisibleChanged);
}
void NewPresentation(Microsoft.Office.Interop.PowerPoint.Presentation oPres)
{
PowerPoint.Application app = this.Application;
PowerPoint.DocumentWindow docWin = null;
foreach (PowerPoint.DocumentWindow win in Globals.ThisAddIn.Application.Windows)
{
if (win.Presentation.Name == app.ActivePresentation.Name)
{
docWin = win;
}
}
this.taskPaneControl1 = new TaskPaneControl();
this.taskPaneValue = this.CustomTaskPanes.Add(taskPaneControl1, "MyCustomTaskPane", docWin);
this.taskPaneValue.VisibleChanged += new EventHandler(taskPaneValue_VisibleChanged);
}
private void taskPaneValue_VisibleChanged(object sender, System.EventArgs e)
{
Globals.Ribbons.ManageTaskPaneRibbon.toggleButton1.Checked =
taskPaneValue.Visible;
}
public Microsoft.Office.Tools.CustomTaskPane TaskPane
{
get
{
return taskPaneValue;
}
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
I remember the learning curve oh so well. Here's a sample that I believe addresses your issue. You need to link the task pane to the document. I relied on the naming scheme for new documents here, but a DocumentVariable would be a much better choice (they are discarded at the end of the current session). Add a variable to the presentation, store the task pane id in the control, and compare them to get the right task pane.
You need an XML ribbon (could probably use a Ribbon Designer but those are not as good). I removed some of the boilerplate and irrelevant code from this.
ThisAddIn.cs:
namespace PowerPointAddIn1
{
public partial class ThisAddIn
{
public static int counter = 0;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
this.Application.AfterNewPresentation += Application_AfterNewPresentation;
}
private void Application_AfterNewPresentation(PowerPoint.Presentation Pres)
{
int count = ++counter;
UserControl1 uc = new UserControl1("task pane " + count);
CustomTaskPane ctp = CustomTaskPanes.Add(uc, "custom task pane " + count);
ctp.Visible = true;
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
{
return new Ribbon1();
}
}
}
Ribbon1.cs:
namespace PowerPointAddIn1
{
[ComVisible(true)]
public class Ribbon1 : Office.IRibbonExtensibility
{
private Office.IRibbonUI ribbon;
public Ribbon1()
{
}
public void toggleTaskPane(Office.IRibbonControl control, bool enabled)
{
var CTPs = Globals.ThisAddIn.CustomTaskPanes;
var pres = Globals.ThisAddIn.Application.ActivePresentation;
foreach (var x in CTPs)
{
if (pres.Name.EndsWith(x.Title.Replace("custom task pane ", "")))
{
x.Visible = enabled;
}
}
}
public bool isPressed(Office.IRibbonControl control)
{
var CTPs = Globals.ThisAddIn.CustomTaskPanes;
var pres = Globals.ThisAddIn.Application.ActivePresentation;
foreach (var x in CTPs)
{
if (pres.Name.EndsWith(x.Title.Replace("custom task pane ", "")))
{
return x.Visible;
}
}
return false;
}
}
}
Ribbon1.xml:
<?xml version="1.0" encoding="UTF-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Ribbon_Load">
<ribbon>
<tabs>
<tab idMso="TabAddIns">
<group id="MyGroup"
label="My Group">
<checkBox id="mycheckbox" label="show task pane" onAction="toggleTaskPane" getPressed="isPressed" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
UsreControl1.cs (just has a label on it):
namespace PowerPointAddIn1
{
public partial class UserControl1 : UserControl
{
public UserControl1(string labelValue)
{
InitializeComponent();
label1.Text = labelValue;
}
}
}
I just want to share my results which works now for me (Thanks to Chris who gave me some valuable inputs). I do have a customtaskpane management which works for each presentation. The only Thing which is not yet implemented is if a user opens the document in a separate window (View / New Window). This one I don't know how to manage.
As fare as I can test it this works now.
here is the link to the whole solution:
https://happypc-my.sharepoint.com/personal/roger_heckly_happy-pc_ch/_layouts/15/guestaccess.aspx?docid=0426d40dc5df74d66ba42a3b928111ce8&authkey=Aa6yX6QWUnqXp1jcUfGveL8
Please be Aware - I'm beginner - so if you have feedback / inputs please let me know. For sure, some code could be written easier etc.

How to know the input buffer of the serial port has information, in C#?

I am building a program in C# to be used in one of my course at a college to demonstrate how Asynchronous connections work using RS-232 and two computers connected together. My course is not about programming, but data networks, so the connectivity is what I am looking for.
picture 1 - sample layout of GUI using Visual Studio 2015
One of the features I want to implement in my program is to show how a Master-slave, simplex connection works (i.e. the program can choose between been a master to send input from the keyboard; or slave to only receive information and print it on a textbox).
What I have already is the capability of initializing the serial port with specific characteristics (baud rate, data bits, stop bits, etc). This features are selected using combo boxes from the GUI, and assigned to the port when the user clicks a button to "open the port".
What I don't know is how to create the "slave" part of the program. My idea of what I could do is, after you choose the program to be "slave", you open the port waiting for some sort of flag or event to trigger when the input buffer has data stored.
I've been reading several forums and I can't find anything similar to what I need. I have, however, tested multiple alternatives that I believed would bring me closer to what I need with little to no result. I come to ask for an idea of what I could be doing wrong, or suggestions on how to tackle this problem. The problematic lines are bolded (or 2 stars ( * ) ):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace SerialCommTester
{
public partial class frmSerialComm : Form
{
static SerialPort _PuertoSerial;
public frmSerialComm()
{
InitializeComponent();
getAvailablePorts();
}
//---------------------------------my functions--------------------------------------
void getAvailablePorts()
{
string[] ports = SerialPort.GetPortNames();
cmbPortList.Items.AddRange(ports);
}
void activatePort()
{
//Note that all the combo boxes are named somewhat accordingly to what the information they are meant to display.
if (cmbPortList.Text != "" && cmbBaudRate.Text != "" && cmbParity.Text != "" && cmbStopBits.Text != "")
{
_PuertoSerial.PortName = cmbPortList.Text;
_PuertoSerial.BaudRate = Convert.ToInt32(cmbBaudRate.Text);
_PuertoSerial.RtsEnable = true;
_PuertoSerial.DtrEnable = true;
_PuertoSerial.DataBits = Convert.ToInt32(cmbDataBits.Text);
if (cmbParity.Text == "Even") { _PuertoSerial.Parity = Parity.Even; }
else if (cmbParity.Text == "Odd") { _PuertoSerial.Parity = Parity.Odd; }
else if (cmbParity.Text == "Space") { _PuertoSerial.Parity = Parity.Space; }
else if (cmbParity.Text == "Mark") { _PuertoSerial.Parity = Parity.Mark; }
else { _PuertoSerial.Parity = Parity.None; }
if (cmbStopBits.Text =="2") { _PuertoSerial.StopBits = StopBits.Two; }
else if (cmbStopBits.Text == "1.5") { _PuertoSerial.StopBits = StopBits.OnePointFive; }
else { _PuertoSerial.StopBits = StopBits.One; }
if (cmbHandShake.Text == "Software Flow Control") { _PuertoSerial.Handshake = Handshake.XOnXOff; }
else if (cmbHandShake.Text == "Hardware Flow Control") { _PuertoSerial.Handshake = Handshake.RequestToSend; }
else { _PuertoSerial.Handshake = Handshake.None; }
_PuertoSerial.ReadTimeout = 500;
_PuertoSerial.WriteTimeout = 500;
_PuertoSerial.Open();
//in my understanding, this line of code is needed to handle data being received. Does it trigger a flag or something?
**_PuertoSerial.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);**
}
else
{
txtRecieve.Text = "Input selection missing 1 or more characteristics";
}
}
**
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort testing = (SerialPort)sender;
txtRecieve.AppendText(testing.ReadExisting()); //txtRecieve cannot be reached within this function. It indicates the following error: "An object reference is required for the non-static field, method, or property 'frmSerialComm.txtRecieve'
}
**
void enableDisableGUI(bool[] input)
{
grpConnection.Enabled = input[0];
grpCharacteristics.Enabled = input[1];
btnOpenPort.Enabled = input[2];
btnClosePort.Enabled = input[3];
txtSend.Enabled = ((cmbControlMasterSlave.Text == "Slave") ? false : true);
}
//----------------------------C# objects / functions--------------------------------------
private void btnOpenPort_Click(object sender, EventArgs e)
{
try
{
_PuertoSerial = new SerialPort();
activatePort();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Message ", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
bool[] format = { false, false, false, true};
enableDisableGUI(format);
}
private void btnClosePort_Click(object sender, EventArgs e)
{
_PuertoSerial.Close();
bool[] format = { true, true, true, false};
enableDisableGUI(format);
}
private void txtSend_KeyPress(object sender, KeyPressEventArgs e)
{
_PuertoSerial.Write(e.KeyChar.ToString()); //this is how I send data through the serial port.
}
private void btnClearTxts_Click(object sender, EventArgs e)
{
txtRecieve.Clear();
txtSend.Clear();
}
} //class closes
} //program closes
I am not an experienced programmer, I just want to create something useful for my students. Any constructive criticism will be highly appreciated.
I don't have any definitive answers for you. You code looks like it should provide what you need once you get past the two possible glitches.
I think you should attach your SerialDataReceivedEventHandler BEFORE
you call _PuertoSerial.Open().
It may have no effect since event handlers can normally be enabled/disabled dynamically, but I base the advice on the following comment taken from the .Net source code for SerialPort on MSDN.
// all the magic happens in the call to the instance's .Open() method.
// Internally, the SerialStream constructor opens the file handle, sets the device control block and associated Win32 structures, and begins the event-watching cycle.
The "object reference" error might be resolved by removing the
static modifier from your DataReceivedHandler. If not, or if that
static modifier is necessary for some reason, then perhaps the
txtRecieve control has a private modifier which needs to be changed
to internal or public. You should be able to use Visual Studio in
debug mode to step into the InitializeComponent() method and see
where txtRecieve is being instantiated.
Well, I believe that I needed to read more. This is how I solved the problem (if this is not the real solution, at least is working for now):
I moved the "SerialDataReceivedEventHandler" line before the _PuertoSerial.open();
I followed the suggestions from this article:
https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(EHInvalidOperation.WinForms.IllegalCrossThreadCall);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang-csharp)&rd=true
So my funtions (one existings + a new one) look like this:
void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
printReceivedText(_PuertoSerial.ReadExisting());
}
private void printReceivedText(string text)
{
if (this.txtSend.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(printReceivedText);
this.Invoke(d, new object[] { text });
}
else
{
this.txtRecieve.AppendText(text);
_PuertoSerial.DiscardInBuffer();
}
}
For now seems to be working fine. The final testing will come when I connect another terminal and see the program interacting with each other.

Done button not firing Completed event on Xamarin Entry

After I've added the Done button on iOS numeric keyboard in Xamarin Forms, I encountered another problem: the Done button not firing Completed event (like return button does).
On my way to implement this, I found the following code on Xamarin Forums:
using System;
using System.Drawing;
using System.Reflection;
using Xamarin.Forms.Platform.iOS;
using Xamarin.Forms;
using UIKit;
using KeyboardTest.iOS;
using KeyboardTest;
[assembly: ExportRenderer(typeof(EntryDone), typeof(EntryDoneRenderer))]
namespace KeyboardTest.iOS
{
public class EntryDoneRenderer : EntryRenderer
{
// used to cache the MethodInfo so we only have the reflection hit once
private MethodInfo baseEntrySendCompleted = null;
public EntryDoneRenderer ()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged (e);
if (this.Element.Keyboard == Keyboard.Numeric)
{
// only want the Done on the numeric keyboard type
UIToolbar toolbar = new UIToolbar (new RectangleF (0.0f, 0.0f, (float)Control.Frame.Size.Width, 44.0f));
var doneButton = new UIBarButtonItem (UIBarButtonSystemItem.Done, delegate {
this.Control.ResignFirstResponder ();
Type baseEntry = this.Element.GetType();
if(baseEntrySendCompleted==null)
{
// use reflection to find our method
baseEntrySendCompleted = baseEntry.GetMethod("SendCompleted",BindingFlags.NonPublic|BindingFlags.Instance);
}
try
{
baseEntrySendCompleted.Invoke(this.Element,null);
}
catch (Exception ex)
{
// handle the invoke error condition
}
});
toolbar.Items = new UIBarButtonItem[] {
new UIBarButtonItem (UIBarButtonSystemItem.FlexibleSpace),
doneButton
};
this.Control.InputAccessoryView = toolbar;
}
}
}
}
I don't know why, but I receive the error:
System.NullReferenceException: Object reference not set to an instance of an object
at myGame.iOS.DoneEntryRenderer.<OnElementChanged>m__0 (System.Object , System.EventArgs ) [0x0005d] in /Users/silviu/Projects/myGame/iOS/DoneEntry.cs:37
On that line I have the code:
baseEntrySendCompleted.Invoke(this.Element, null);
I’ve tried to debug the problem and I found that the SendCompleted method does not exist, but I don’t understand how to solve this problem in the lastest version of Xamarin, because I think on the moment when that guy posted the code, worked.
Thanks!
SendCompleted() was actually added for IEntryController so you don't need to use reflection for this any longer. In fact it appears that way no longer works. Just call SendCompleted() directly from your button press like so.
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
var toolbar = new UIToolbar(new CGRect(0.0f, 0.0f, Control.Frame.Size.Width, 44.0f));
toolbar.Items = new[]
{
new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace),
new UIBarButtonItem(UIBarButtonSystemItem.Done, delegate {
Control.ResignFirstResponder();
((IEntryController)Element).SendCompleted();
})
};
this.Control.InputAccessoryView = toolbar;
}

Capture Camera Feed from EasyCap 4ch USB DVR Device using DirectShow and DirectX.Capture C#

I'm trying to capture the camera feed from an EasyCap 4 Channel USB DVR Device that i got recently
and i have bought a super Mimi Monochrome/Color Camera and connected it to the DVR Device and managed to correctly setup the device with the driver "SMI Grabber" and installed the software that comes with the Device "SuperViewer"
and i have wrote a simple windows form application that contains a PictureBox to priview the camera feed
(There is an edit in the bottom)
The Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DirectX.Capture;
namespace DirectShowWithCrossbar
{
public partial class Form1 : Form
{
private CrossbarSource crossbar;
private Filters filters;
private Capture capture;
public Form1()
{
InitializeComponent();
filters = new Filters();
capture = new Capture(filters.VideoInputDevices[0], filters.AudioInputDevices[0]);
foreach (Filter device in filters.VideoInputDevices)
{
comboBox1.Items.Add(device);
}
if (comboBox1.Items.Count > 0)
comboBox1.SelectedIndex = 0;
foreach (Filter device in filters.AudioInputDevices)
{
comboBox2.Items.Add(device);
}
if (comboBox2.Items.Count > 0)
comboBox2.SelectedIndex = 0;
foreach (Source source in capture.VideoSources)
{
comboBox3.Items.Add(source);
}
if (comboBox3.Items.Count > 0)
comboBox3.SelectedIndex = 0;
ShowPropertPagesInMenuStrip();
crossbar = (CrossbarSource)capture.VideoSource;
crossbar.Enabled = true;
capture.PreviewWindow = pictureBox1;
}
private void ShowPropertPagesInMenuStrip()
{
foreach (PropertyPage pro in capture.PropertyPages)
{
menusToolStripMenuItem.DropDownItems.Add(new ToolStripMenuItem(pro.Name));
}
}
private void button1_Click(object sender, EventArgs e)
{
capture.Cue();
capture.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
capture.Stop();
capture.Dispose();
}
private void comboBox3_SelectedIndexChanged(object sender, EventArgs e)
{
capture.VideoSource = (Source)comboBox3.SelectedItem;
}
}
}
and i got a black screen in the picture box ??
and by mistake after closing my application i ran the SuperViewer application that comes with the DVR device and then open my application then my picture box began to show me the feed from the camera, strange!!! and the feed from the original software freezes !!
DirectX.Capture Example and Sources tried with the same result http://www.codeproject.com/Articles/3566/DirectX-Capture-Class-Library
and i have also used OpenCV and Touchless and i got the same result :(
Edit:
I have been searching and found that i need to get the filter (IAMCrossbar) i think that is the problem DirectShow USB webcam changing video source and after appling the changes in this link in the DirectX.Capture Wrapper i still get the same results :(
Thanks for any help in advance Yaser
If your capture device has option, to select one from multiple input interfaces, then yes, you are right about, that you needed to use IAMCrossbar.
If you want to stick to Directshow( as others have suggested OpenCV), then I would suggest,
Try building all capturing related code in a C++/CLI dll,
Build all your UI in C#.
You can take this MP3 Player Sample Project as starting point for your dll.
For capturing, AmCap is a detailed example.
What I mean is that you need to get the capturing code out of AmCap into above MP3 Player Sample dll, and expose it to your C# application.

Refactor code to use Custom class instead of the one the System provides

Well, this is probably the dumbest question yet, but I have a huge problem that bothers me. First off, I used the code sample from Mick Doherty's Tab Control Tips under reposition TabItems at runtime to allow drag and drop of my tabs in my control. The problem is I use a custom TabPage class by the name of ExtendedTabPage and this causes me trouble. I tried casting or using the as keyword but I am out of luck, so I would like someone to help me out on how to refactor the code to allow for dragging of the custom tabs.
EDIT: I forgot to mention that ExtendedTabPage is an abstract class that is inherited by my objects (for example one of my Windows belongs to ConsoleTab class that inherits ExtendedTabPage). Is this any relevant to the problem itself?
EDIT 2: Major discovery - In the DragOver method, if I try using ConsoleTab in the typeof statements, it seems to work perfectly fine. The problem is that I do not want to do this specifically for this class but for all classes inheriting from its parent class, which is abstract (and I can actually convert it to non-abstract if need be but I won't actively use it...).
EDIT 3: Maybe a good way would be to directly swap using the indexes and avoid using the TabPage Data, however I am a bit confused on how to do this...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ReCodeConsole.Properties;
namespace ReCodeConsole.Core
{
/// <summary>
/// Implements all the extra functionality needed for tab pages on top of the existing TabControl.
/// Includes events for DrawItem, MouseMove and MouseDown.
/// </summary>
public partial class ExtendedTabControl : TabControl
{
/// <summary>
/// Initializes a new instance of the ExtendedTabControl class. All events are added.
/// </summary>
public ExtendedTabControl() :base()
{
this.DrawItem+=new DrawItemEventHandler(DrawTab);
this.MouseClick+=new MouseEventHandler(Tab_OnMouseDown);
this.MouseMove+=new MouseEventHandler(Tab_OnMouseMove);
this.DragOver+=new DragEventHandler(Tab_OnDragOver);
}
/// <summary>
/// Used to store the starting position of a tab drag event.
/// </summary>
private Point DragStartPosition = Point.Empty;
private void DrawTab(object sender, DrawItemEventArgs e)
{
//
//This code will render the close button at the end of the Tab caption.
//
e.Graphics.DrawImage(Resources.TabCloseButton, e.Bounds.Right - 22, e.Bounds.Top + 5, 14, 14);
e.Graphics.DrawString(this.TabPages[e.Index].Text, e.Font, Brushes.Black, e.Bounds.Left + 12, e.Bounds.Top + 3);
e.DrawFocusRectangle();
}
private void Tab_OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
//
// Regardless of where the MouseDown event originated, save the coordinates for dragging.
//
DragStartPosition = new Point(e.X, e.Y);
#region Close Button Handling
//
// Close button code - looping through the controls.
//
for (int i = 0; i < this.TabPages.Count; i++)
{
Rectangle r = GetTabRect(i);
//
//Getting the position of the close button.
//
Rectangle closeButton = new Rectangle(r.Right - 22, r.Top + 5, 14, 14);
if (closeButton.Contains(e.Location))
{
if (this.TabPages[i] is ExtendedTabPage)
{
if ((this.TabPages[i] as ExtendedTabPage).IsCloseable)
{
if (MessageBox.Show("Are you sure you want to close this tab?", "Close", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
this.TabPages.RemoveAt(i);
break;
}
}
}
else
{
if (MessageBox.Show("Are you sure you want to close this tab?", "Close", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
this.TabPages.RemoveAt(i);
break;
}
}
}
}
#endregion
}
private TabPage HoverTab()
{
for (int index = 0; index <= TabCount - 1; index++)
{
if (GetTabRect(index).Contains(PointToClient(Cursor.Position)))
return (TabPage)TabPages[index];
}
return null;
}
private void Tab_OnDragOver(object sender, System.Windows.Forms.DragEventArgs e)
{
TabPage hover_Tab = HoverTab();
if (hover_Tab == null)
e.Effect = DragDropEffects.None;
else
{
if (e.Data.GetDataPresent(typeof(TabPage)))
{
e.Effect = DragDropEffects.Move;
TabPage drag_tab = (TabPage)e.Data.GetData(typeof(TabPage));
if (hover_Tab == drag_tab) return;
Rectangle TabRect = GetTabRect(TabPages.IndexOf(hover_Tab));
TabRect.Inflate(-3, -3);
if (TabRect.Contains(PointToClient(new Point(e.X, e.Y))))
{
SwapTabPages(drag_tab, hover_Tab);
SelectedTab = drag_tab;
}
}
}
}
private void Tab_OnMouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
Rectangle r = new Rectangle(DragStartPosition, Size.Empty);
r.Inflate(SystemInformation.DragSize);
TabPage tp = HoverTab();
if (tp != null)
{
if (!r.Contains(e.X, e.Y))
DoDragDrop(tp, DragDropEffects.All);
}
DragStartPosition = Point.Empty;
}
private void SwapTabPages(TabPage tp1, TabPage tp2)
{
int Index1 = this.TabPages.IndexOf(tp1);
int Index2 = this.TabPages.IndexOf(tp2);
this.TabPages[Index1] = tp2;
this.TabPages[Index2] = tp1;
}
}
}
Now ignore anything that is extra, I give you the whole code in case anything else messes it all up. So to recap, I want someone to fix the code or at least explain how to do so (or if it isn't possible what to do) in order for me to be able to swap between ExtendedTabPage items instead of regular TabPage. I have tried and regular TabPage objects work, whereas my custom ones don't. If the solution has to include only one of them (so normal TabPages cannot work), go for only ExtendedTabPage as I can convert the rest of the stuff into that. I hope this is not like really dumb and very much a situation where I am overlooking something.
P.S: I also checked the page I linked for a solution that worked for custom classes, but with no luck, as it caused me twice the problems and half my code broke even with the proper assembly references, so that is not a real option. :/
Well, as nobody seems to know the solution and after excessive testing and tweaking plus some very lucky finds in the site, namely How to deal with GetDataPresent to let it accept all the derived types and C# Drag and Drop - e.Data.GetData using a base class, I am happy to report that the problem can easily enough be solved by substituting the GetPresentData and GetData calls. I provide the tweaked code for Tab_OnDragOver which was where the problem originated and I hope it all works fine for anyone who hits this page and looks for a solution!
private void Tab_OnDragOver(object sender, System.Windows.Forms.DragEventArgs e)
{
TabPage hover_Tab = HoverTab();
if (hover_Tab == null)
e.Effect = DragDropEffects.None;
else
{
var drag_tab = e.Data.GetData(e.Data.GetFormats()[0]);
if (typeof(TabPage).IsAssignableFrom(drag_tab.GetType()))
{
e.Effect = DragDropEffects.Move;
if (hover_Tab == drag_tab) return;
Rectangle TabRect = GetTabRect(TabPages.IndexOf(hover_Tab));
TabRect.Inflate(-3, -3);
if (TabRect.Contains(PointToClient(new Point(e.X, e.Y))))
{
SwapTabPages(drag_tab as TabPage, hover_Tab);
SelectedTab = drag_tab as TabPage;
}
}
}
}

Categories