Mostrando entradas con la etiqueta c#. Mostrar todas las entradas
Mostrando entradas con la etiqueta c#. Mostrar todas las entradas

jueves, 4 de noviembre de 2010

Move items from one listbox to another listbox without pain in C#

I blamed Java Swing more than once for the lack of rich controls for my GUIs but the Microsoft world is not an exception to the rule. In most application I have written in .Net, I required a component for asign and deallocate objects and I'm sure I'm not the only one human being with this need, sadly there's no official control that provides this functionality (or at least I haven't found it).

My solution was use two ListBoxes (one for the asigned items and one for the deallocated items) and put some buttons between them, you know the classic asign all, asign selection, deallocate all and finally deallocate all. To avoid writing multiple times the same move item code I created two gold rules to operate my list boxes:

1. You have to provide the items to the ListBoxes binding a generic List.
2. The items should be objects with a correct overriden Equals method.

The mandatory screenshot:





The events of all buttons:


private void asignAllButton_Click(object sender, EventArgs e)
{
moveAllItems(notAsignedListBox,asignedListBox);
}

private void asignSelectionButton_Click(object sender, EventArgs e)
{
moveSelectedItems<MyObject>(notAsignedListBox, asignedListBox);
}

private void deallocateSelectionButton_Click(object sender, EventArgs e)
{
moveSelectedItems<MyObject>(asignedListBox, notAsignedListBox);
}

private void deallocateAllButton_Click(object sender, EventArgs e)
{
moveAllItems<MyObject>(asignedListBox, notAsignedListBox);
}


The utility methods:


public static void moveSelectedItems<T>(ListBox source, ListBox destiny)
{
var selectedList = source.SelectedItems;
if (selectedList == null || selectedList.Count == 0)
{
return;
}
var destinyList = (List<T>)destiny.DataSource;
var sourceList = (List<T>)source.DataSource;
if (destinyList == null)
{
destinyList = new List<T>();
}
foreach (T generic in selectedList)
{
destinyList.Add(generic);
sourceList.Remove(generic);
}
destiny.DataSource = null;
source.DataSource = null;
destiny.DataSource = destinyList;
source.DataSource = sourceList;
}

public static void moveAllItems<T>(ListBox source, ListBox destiny)
{
if (source.DataSource == null)
{
return;
}
var destinyList = (List<T>)destiny.DataSource;
if (destinyList == null)
{
destinyList = new List<T>();
}
destinyList.AddRange((List<T>)source.DataSource);
destiny.DataSource = null;
destiny.DataSource = destinyList;
source.DataSource = null;
}


Now you have a cross object asignation component. God! My favorite programing language is Java but my blog is being invaded by C# posts ! :S

domingo, 31 de octubre de 2010

Avoid ListView flicker and scrollbar reset when refreshing its items

If you need to refresh the data of a listview frequently (every 20 seconds, for example) we don't recommed you use the listview "clear()" method and set again all the items. This approach have two major drawbacks.

1. You will be experimenting an annoying flicket every time you call "clear()" method.
2. The scrollbars of the listview will be reseted to (0,0) position.

To avoid this behaivior we recommed you instead of clearing everything, just add new items and remove the ones that you don't need anymore.

Our implementation mantains all the time a second generic list synchronized with the listview, this is a better aproach to us 'cause all the items are obtained from the database and we minimize the number of access to database.

First our ListView will be displaying MyObjects:

public class MyObject : IEquatable
{
public int id { get; set; }
public bool Equals(MyObject other)
{
if (Object.ReferenceEquals(other, null)) return false;
if (Object.ReferenceEquals(this, other)) return true;
return id.Equals(other.id);
}
public override int GetHashCode()
{
return id;
}
}


The code that populates the list and refresh it:


void refresh(){
//findMyObjects return the new IList that we want to sync
var myObjectsNew = findMyObjects();
var myObjectsToAdd = deleteItemsListView(myObjectsOld,myObjectsNew,listView);
foreach (MyObject myObjectToAdd in myObjectsToAdd)
{
var item = new ListViewItem(myObjectToAdd.id.ToString());
item.Name = myObjectToAdd.id.ToString();
listView.Items.Add(item);
myObjectsOld.Add(myObjectToAdd);
}
}


The delete method (returns the elements to add):


public static IList deleteItemsListView(IList oldList, IList newList, ListView listView)
{
var intersection = newList.Intersect(oldList);
var itemsToAdd = newList.Except(intersection);
var itemsToRemove = oldList.Except(intersection);
for (int i = itemsToRemove.Count() - 1; i > -1; i--)
{
T objetoParaEliminar = itemsToRemove.ToList()[i];
for (int j = 0; j < listView.Items.Count; j )
{
if (listView.Items[j].Name == objetoParaEliminar.GetHashCode().ToString())
{
listView.Items.RemoveAt(j);
break;
}
}
for (int j = 0; j < oldList.Count; j )
{
if (oldList[j].Equals(objetoParaEliminar))
{
oldList.RemoveAt(j);
break;
}
}
}
return itemsToAdd.ToList();
}
}


Complex? Yes, a little. It would be terrific having a generic ListViewItem in the ListView with a "sync()" method but thats not the case, another solution could be binding the list to a datasource like this guy in "code project":

http://www.codeproject.com/KB/list/ListView_DataBinding.aspx

To be honest, his implementation is more elegant but to our purposes this is enough.

Important points in our implementation:

1. All your items must have a unique id and must implement IEquatiable interface (we are using linq).
2. We assume that if the id is already in the list view this is synchronized. This means that we only add and delete new ids but didn't verify if item changed.
3. If you reorder in some way the ListView you can't ensure that the generic list can be acceded using the indices of the listview.
4. myObjectsOld generic list should be class scoped and should be the same reference to every call to refresh method.

lunes, 18 de octubre de 2010

TransparencyKey and Backcolor inherited automatically to buttons and checkboxes

This is a warning more than a bug of Visual Studio. If you set the the BackColor and TransparencyKey to a WindowsForm both properties will be inherited automatically to buttons and checkboxes that lie inside this form.

Ex.

1. You have WindowForm1 and this Form contains button1 and checkbox1.
2. You set WindowForm1 BackColor an TransparencyKey to "Lime".
3. button1 and checkbox1 will have BackColor = "Lime" TransparencyKey = "Lime" automatically.

That's it. So why should I consern about it? Well the problem is that when you are working in XP for example with all the visual effects activated the BackColor of all windows controls is overriden by a fancy 3d color ( "fancy" for windows :) ) but if you deactivate all the visual effects, the background of your components will be the BackColor defined.

So? Well if you disable the visual effects and the BackColor of the buttons is transparent you can't click the buttons because like the api of TransparencyKey says, "the actions over the transparency area are sended to the back form".

Simply, if you are using TransparencyKey in a form, don't forget to change the backColor of your buttons and checkboxes to "ControlLight" and always is a good practice to test out ALL your forms with and without effects.

Ps. In fact, you don't need to deactivate all the effects, you just need to deactivate the one that overrides the backcolor of the components, to be honest I'm lazy to research what effect is.

domingo, 10 de octubre de 2010

TransparencyKey unsupported when using double buffer.

One of the easiest way to create transparent forms in Windows is using the Properties TrasparencyKey and BackColor of a WindowForm but after some hours struggling with strange behaiviors I discovered that TransparencyKey property is unsupported when enabling DoubleBuffer.

I can't beleive Microsoft couldn't mention it in ther official API:

http://msdn.microsoft.com/es-es/library/system.windows.forms.form.transparencykey%28VS.80%29.aspx

lunes, 16 de agosto de 2010

Some thoughts about byte data types in Java and C#

We developed a simple communication application between JME and C# using sockets. We send some ASCII data from a C# to a java midlet, this data can contain printable ASCII (from 0 to 128) and non printable ASCII (from 129 to 255).

Everything was working smooth until we had to extract some bytes from the frame in the java midlet and compare them to non printable ASCII. Example:


byte x =byte[30]; //This position contains a non printable Happy Face ASCII

if(x == 254){
//This condition never is true
}


This happen because the java byte is signed, this means that this goes from -127 to 128, in the other hand c# byte is non-signed (from 0 to 255). Our first aproach was to convert all the byte arrays in java to short arrays adding 256 to negative ones, but thats not really necesary.

The rule is easy, it doesn't matter what operation you are doing over the signed byte, adding, substracting, multiplying, etc, always the result will be the same even if it is signed or unsigned. Why? The sign is in your mind! The sign is no more than an interpretation. But what happend when you want to compare this values or print them? In that case this will not work because casting a negative byte to char in java is not the same as deleting the sign.

Another example:


char x = (char)-1;

//x is not 255.


Whats the correct way to proceed?

Easy, you need to add, substract, multiply, etc signed or unsigned bytes? Don't worry do it, it will work always and even if this is signed or unsigned you will get exactly the same result. You need to compare the chars obtained? Delete the sign of the signed byte usign this line:


byte x = -1;
char y = (char)(x & 0xFF)


Sweet..