Knowledge Base Nr: 00282 compare.cs - http://www.swe-kaiser.de

Downloads:

c#: eigene controls durch kombination mehrerer standardcontrols und überladen von OnPaint()

  
//das control vergleich zwei arrays (z.b. textfiles) und stellt die unterschiede farblich dar
//stichwort: windiff für arme.
//die zwei ownerdraw listboxen werden synchronisiert und die ausgewählte zeile
//am unteren rand des controls nochmal untereinander angezeigt. für die 2 listboxen können titel
//vergeben werden. das control passt sich bei größenanderungen automaisch an.

//um ein zusätzliches binary (dll für control) zu vermeiden (nachteil keine unterstützung des
//controls im design-mode von visual studio) und trotzdem einen gewissen komfort beim entwurf zu
//bieten habe ich mich entschieden das control automatisch auf die größe seines Parent auszurichten.
//d.h. im designer kann z.b. ein panel als platzhalter für das neue control verwendet werden.

//usage:
//create control
KCompare compareLB = new KCompare();
//customize colors
compareLB.colTitle = Color.Green;
compareLB.colBGTitle = Color.Red;
...

//create a panel within design-mode with the prefered size or dock-style
panel1.Controls.Add(compareLB);
this.ResumeLayout();

//create data to display and fill control
object[] leftContent = new object[] {
"012lulli78ABCDEFGHIJKLMNOPIJKLMNOPQRSTUVWXYZ01QRYZ01234567890",
"012345678AIJKLMNOPQVWXYZ01BCDEFGHIJKLMNOPQRSTUVW34567890",
"012IJKLMNOPQRSTUVWXYZ01345678ABCDEFGHIJKLMNOPQRSZ01234567890"
};
object[] rightContent = new object[] {
"012345678ABCDEFGHIJKLMNOPIJKLMNOPQRSTUVWXYZ01QRSZ01234567890",
"012IJKLMNOPQRSTUVWXYZ01345678ABCDEFGHIJKLMNOPQRSZ01234567890",
};
bool isEqual = compareLB.Fill("linkes array", leftContent, "rechtes array", rightContent);

//control:
public class CompareControl : Panel
{
private Label lblLeft;
private Label lblRight;
private KCompareTextBox tbxLeft;
private KCompareTextBox tbxRight;
private ListBox lbxLeft;
private ListBox lbxRight;
private HScrollBar hScrollBar;
private Timer tmrSynchronizeListBoxes;
private System.ComponentModel.IContainer components;

//the colors are public so they can be set from outside!
public Brush brBackground = Brushes.White;

public Color colTitle = Color.Black;
public Color colBGTitle = Color.LightGray;

public Brush brMissing = Brushes.Red;
public Brush brBGMissing = Brushes.Yellow;

public Brush brDifferent = Brushes.Green;
public Brush brBGDifferent = Brushes.Yellow;

public Brush brEqual = Brushes.Black;
public Brush brBGEqual = Brushes.White;

private int scrollHPos = 0;
private int scrollVPosLB1 = 0;
private int scrollVPosLB2 = 0;
private bool bTimerLocked = false;
private int gnHeightTB = -1;

public CompareControl()
{
InitializeComponent();
}

protected override void Dispose( bool disposing )
{
...
}

private void InitializeComponent()
{
...
}

public bool Fill(string leftTitle, object[] leftContent, string rightTitle, object[] rightContent)
{
SuspendLayout();

int max = 0;

lblLeft.Text = leftTitle;
lblRight.Text = rightTitle;

lbxLeft.Items.AddRange(leftContent);
lbxRight.Items.AddRange(rightContent);

for (int i=0; i<lbxLeft.Items.Count; i++)
{
if (max < lbxLeft.Items[i].ToString().Length)
max = lbxLeft.Items[i].ToString().Length;
}
for (int i=0; i<lbxRight.Items.Count; i++)
{
if (max < lbxRight.Items[i].ToString().Length)
max = lbxRight.Items[i].ToString().Length;
}

hScrollBar.Maximum = max;

//quick check for differences
bool isEqual = true;

if (leftContent.Length != rightContent.Length)
{
isEqual = false;
}
else
{
for (int i=0; i<leftContent.Length; i++)
{
if (leftContent[i].ToString() != rightContent[i].ToString())
{
isEqual = false;
break;
}
}
}

ResumeLayout(false);
return isEqual;
}

//can be overwritten and customized (compares two characters -
//returns 0 if equal and -1(Missing) or 1(Different) otherwise)
public int CompareTextBoxLines(int nColNo)
{
if ((nColNo >= tbxLeft.Text.Length)
|| (nColNo >= tbxRight.Text.Length))
return -1;

int diff = Compare(tbxLeft.Text[nColNo], tbxRight.Text[nColNo]);

return diff;
}

//can be overwritten and customized (compares two characters -
//returns 0 if equal or 1 if different)
protected int Compare(char leftChar, char rightChar)
{
if (leftChar != rightChar)
return 1;

return 0;
}

//can be overwritten and customized (compares two characters -
//returns 0 if equal and -1(Missing) or 1(Different) otherwise)
protected int CompareListBoxLines(int nLineNo, int nColNo)
{
if ((nLineNo >= lbxLeft.Items.Count)
|| (nLineNo >= lbxRight.Items.Count))
return -1;

if ((nColNo >= lbxLeft.Items[nLineNo].ToString().Length)
|| (nColNo >= lbxRight.Items[nLineNo].ToString().Length))
return -1;

int diff = Compare(lbxLeft.Items[nLineNo].ToString()[nColNo]
, lbxRight.Items[nLineNo].ToString()[nColNo]);

return diff;
}

private void panel_Resize(object sender, EventArgs e)
{
Size size = Size;

int nHeightL = lblLeft.Size.Height;
int nHeightTB = tbxLeft.Size.Height;
int nHeightSB = hScrollBar.Size.Height;
int nWidthLB = size.Width / 2;
int nHeightLB = size.Height - nHeightSB - nHeightL - 2*nHeightTB;

if (gnHeightTB < 0)
gnHeightTB = nHeightTB;
else
nHeightTB = gnHeightTB;

lblLeft.Location = new Point(0, 0);
lblLeft.Size = new Size(nWidthLB, nHeightL);

lblRight.Location = new Point(nWidthLB, 0);
lblRight.Size = new Size(nWidthLB, nHeightL);

lbxLeft.Location = new Point(0, nHeightL);
lbxLeft.Size = new Size(nWidthLB, nHeightLB);

lbxRight.Location = new Point(nWidthLB, nHeightL);
lbxRight.Size = new Size(nWidthLB, nHeightLB);

hScrollBar.Location = new Point(0, nHeightLB + nHeightL);
hScrollBar.Size = new Size(size.Width, nHeightSB);

tbxLeft.Location = new Point(0, nHeightL + nHeightLB + nHeightSB);
tbxLeft.Size = new Size(size.Width, gnHeightTB-1);

tbxRight.Location = new Point(0, nHeightL + nHeightLB + nHeightSB + nHeightTB);
tbxRight.Size = new Size(size.Width, gnHeightTB-1);
}

private void DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
ListBox listbox = (ListBox)sender;
Font font = listbox.Font;

SuspendLayout();

e.DrawBackground();

e.Graphics.FillRectangle(brBackground, e.Bounds);

bool selected = ((e.State & DrawItemState.Selected) == DrawItemState.Selected) ? true : false;

string displayText = listbox.Items[e.Index].ToString();

Size cs = TextRenderer.MeasureText("Q", Font);
int fontWidth = cs.Width;
int fontHeight = cs.Height;

Point pos = new Point(e.Bounds.X, e.Bounds.Y);

//e.Graphics.DrawString(displayText, font, Brushes.Black, pos); //e.Bounds);
for (int i=0; i<displayText.Length; i++)
{
int startPos = i - scrollHPos;
if (startPos >= 0)
{

pos.X = startPos * fontWidth;

int nDiff = CompareListBoxLines(e.Index, i);

Brush textBrush;
Brush textBGBrush;

if (nDiff < 0)
{
textBrush = brMissing;
textBGBrush = brBGMissing;
}
else if (nDiff > 0)
{
textBrush = brDifferent;
textBGBrush = brBGDifferent;
}
else
{
textBrush = brEqual;
textBGBrush = brBGEqual;
}

Rectangle rect = new Rectangle(pos, new Size(fontWidth, fontHeight));
e.Graphics.FillRectangle(textBGBrush, rect);

e.Graphics.DrawString(displayText.Substring(i, 1), font, textBrush, pos);
}
}

if (selected) //draw focus rectangle
{
//todo: why do e.DrawFocusRectangle() NOT work???
Rectangle rectSelected = e.Bounds;

rectSelected.X = rectSelected.X + 1;
rectSelected.Y = rectSelected.Y + 1;
rectSelected.Width = rectSelected.Width - 2;
rectSelected.Height = rectSelected.Height - 2;
e.Graphics.DrawRectangle(SystemPens.WindowText, rectSelected);
}

ResumeLayout();
}

private void listBox1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
DrawItem(sender, e);
}

private void listBox2_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
DrawItem(sender, e);
}

private void SyncListBox2(int index)
{
if (index < 0)
return;

scrollVPosLB1 = lbxLeft.TopIndex;
if (scrollVPosLB1 < lbxRight.Items.Count)
{
lbxRight.TopIndex = scrollVPosLB1;
if (index < lbxRight.Items.Count)
lbxRight.SelectedIndex = index;
else
lbxRight.SelectedIndex = -1;
}
else
{
lbxRight.TopIndex = 0;
lbxRight.SelectedIndex = -1;
}
}

private void SyncListBox1(int index)
{
if (index < 0)
return;

scrollVPosLB2 = lbxRight.TopIndex;
if (scrollVPosLB2 < lbxLeft.Items.Count)
{
lbxLeft.TopIndex = scrollVPosLB2;
if (index < lbxLeft.Items.Count)
lbxLeft.SelectedIndex = index;
else
lbxLeft.SelectedIndex = -1;
}
else
{
lbxLeft.TopIndex = 0;
lbxLeft.SelectedIndex = -1;
}
}

private void ShowDifferencesWithinTextboxes(int index)
{
if ((index >= 0) && (index < lbxLeft.Items.Count))
tbxLeft.Text = lbxLeft.Items[index].ToString();
else
tbxLeft.Text = "";

if ((index >= 0) && (index < lbxRight.Items.Count))
tbxRight.Text = lbxRight.Items[index].ToString();
else
tbxRight.Text = "";
}

private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
bTimerLocked = true;
SyncListBox2(((ListBox)sender).SelectedIndex);

ShowDifferencesWithinTextboxes(((ListBox)sender).SelectedIndex);
bTimerLocked = false;
}

private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
bTimerLocked = true;

//((ListBox)sender).SelectedItem differs between VS2003 and VS2005 !!!
//calculate the clicked item from mouseevent and height of an entry
int index = e.Y / ((ListBox)sender).ItemHeight;
SyncListBox2(index);

ShowDifferencesWithinTextboxes(index);
bTimerLocked = false;
}

private void listBox2_SelectedIndexChanged(object sender, System.EventArgs e)
{
bTimerLocked = true;
SyncListBox1(lbxRight.SelectedIndex);

ShowDifferencesWithinTextboxes(lbxRight.SelectedIndex);
bTimerLocked = false;
}

private void listBox2_MouseDown(object sender, MouseEventArgs e)
{
bTimerLocked = true;

//((ListBox)sender).SelectedItem differs between VS2003 and VS2005 !!!
//calculate the clicked item from mouseevent and height of an entry
int index = e.Y / ((ListBox)sender).ItemHeight;
SyncListBox1(index);

ShowDifferencesWithinTextboxes(index);
bTimerLocked = false;
}

private void hScrollBar1_ValueChanged(object sender, System.EventArgs e)
{
scrollHPos = ((HScrollBar)sender).Value;
lbxLeft.Invalidate();
lbxLeft.Update();
lbxRight.Invalidate();
lbxRight.Update();
}

private void timer1_Tick(object sender, System.EventArgs e)
{
if (bTimerLocked)
return;

bTimerLocked = true;
if (scrollVPosLB1 != lbxLeft.TopIndex)
{
SyncListBox2(lbxLeft.SelectedIndex);
ShowDifferencesWithinTextboxes(lbxLeft.SelectedIndex);
}

if (scrollVPosLB2 != lbxRight.TopIndex)
{
SyncListBox1(lbxRight.SelectedIndex);
ShowDifferencesWithinTextboxes(lbxRight.SelectedIndex);
}
bTimerLocked = false;
}
}

public class KCompareTextBox : Label //TextBox: causes problems if clicked (font...)
{
public KCompareTextBox()
{
//absolutely necessary to make OnPaint() work
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
}

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);

e.Graphics.FillRectangle(((CompareControl)Parent).brBackground, e.ClipRectangle);

string displayText = Text;

Size cs = TextRenderer.MeasureText("Q", Font);
int fontWidth = cs.Width;
int fontHeight = cs.Height;

Point pos = new Point(e.ClipRectangle.X, e.ClipRectangle.Y);

//e.Graphics.DrawString(displayText, font, Brushes.Black, pos); //e.Bounds);
for (int i=0; i<displayText.Length; i++)
{
pos.X = i * fontWidth;

int nDiff = ((CompareControl)Parent).CompareTextBoxLines(i);

Brush textBrush;
Brush textBGBrush;

if (nDiff < 0)
{
textBrush = ((CompareControl)Parent).brMissing;
textBGBrush = ((CompareControl)Parent).brBGMissing;
}
else if (nDiff > 0)
{
textBrush = ((CompareControl)Parent).brDifferent;
textBGBrush = ((CompareControl)Parent).brBGDifferent;
}
else
{
textBrush = ((CompareControl)Parent).brEqual;
textBGBrush = ((CompareControl)Parent).brBGEqual;
}

Rectangle rect = new Rectangle(pos, new Size(fontWidth, fontHeight));
e.Graphics.FillRectangle(textBGBrush, rect);

e.Graphics.DrawString(displayText.Substring(i, 1), Font, textBrush, pos);
}
}
}