Here I will post problems I and my colleagues met and solutions we found.

Wednesday, December 24, 2008

Sorting in WPF DataGrid

As you could see from my previous post, I am using WPF Toolkit DataGrid now. One of the reason to do it is to sort data. Sure, it is possible to do sorting in ListView, but using DataGrid I can do it with less efforts.

The only problem was that what I had as data source was regular BindingList, that does not support sorting. So, sorting just didn't work.

The solution I found the easiest is using Linq. Since I assigned my data source in code, not XAML, there was no inconvenience at all.

Instead of line


ItemsSource = list


I used


ItemsSource = from item in list select item;

Thursday, December 11, 2008

Changing background color for header in WPF Toolkit DataGrid

Today I decided to look at DataGrid from WPF Toolkit, available at CodePlex. As you may expect, the first thing I needed to do is to change it look. I started from changing background and immediately stuck.

1. Changed Background - didn't work for everything
2. Changed RowBackground - still not everything.
3. And then I added style for DataGridColumnHeader - now I had geader, but still some small rectungles remained gray.
4. And then something strange happened, I added style for DataGridRowHeader with yellow background, but result was completely unexpected. I got what I wanted, but I still don't understand how.

Here is my result.


<dg:DataGrid x:Name="grid" Background="Red" RowBackground="Red" IsReadOnly="True" GridLinesVisibility="None" >
<dg:DataGrid.Resources>
<Style TargetType="{x:Type dgp:DataGridColumnHeader}">
<Setter Property="Background" Value="Red"/>
</Style>
<Style TargetType="{x:Type dgp:DataGridRowHeader}">
<Setter Property="Background" Value="Green"/>
</Style>
</dg:DataGrid.Resources>
<dg:DataGrid.Columns>
<dg:DataGridTextColumn Header="Test column"/>
</dg:DataGrid.Columns>
</dg:DataGrid>

Friday, October 17, 2008

Variable number of parameters in C#

I wish I knew it before, it would save me few hours (don't ask me why).

Apparently it is possible to make variable number of parameters in method using keyword params.

There are obvious limitations, it can be only the last parameters of the same time. But nevertheless, I think it's cool.

There is positive result for me not knowing that. Now I finally figured out how to work with regular expression, see Regex class. This is really powerful tool to have too.

Friday, September 19, 2008

ComboBox - DisplayMemberPath or TextSearch.TextPath

Probably it's just too late already, and I am slow, but I was confused with DisplayMemberPath and TextSearch.TextPath properties.

First, I added some objects to ComobBox.Items. Not using XML, I did it dynamically from C#. It worked find on Vista computer, but then it didn't work under XP. I just got empty values instead of value of ToString() method.

OK, I looked in my favorite book WPF Unleashed, and found that TextSearch.TextPath should be used. Well, it didn't help. It took me a while until I got to try old familiar DisplayMemberPath property and then it worked.

So, the question I have now is why TextSearch.TextPath didn't work? Or if asked in more general way, what are the rules. When I am supposed to use DisplayMemberPath and when TextSearch.TextPath. Anybody?

Wednesday, September 10, 2008

Hyperlinks in WPF Continued

I wrote before about using hyperlinks in WPF. For my new side project I need to switch from hypertext to the simple text depending on availability of URL. Switching to simple text seemed easy, just assigning text value to the Text property worked. Switching back was not obvious for me.

Then I found that TextBlock has Inlines property. The code became this:

tHypertext.Inlines.Clear();
if (!string.IsNullOrEmpty(uri))
{
if (!string.IsNullOrEmpty(value))
tHypertext.Inlines.Add(value);
tHypertext.NavigateUri = new Uri(uri);
tTextBlock.Inlines.Clear();
tTextBlock.Inlines.Add(tWord);
}
else
{
tTextBlock.Text = value;
tHypertext.NavigateUri = null;
}

Where TextBlock and Hypertext are from previouse example

Thursday, September 04, 2008

Keyboard.Modifiers sometimes doesn't work

There are many examples how to implement keyboard hooks in .NET. The one I used as example is http://blogs.vertigo.com/personal/ralph/Blog/archive/2007/02/12/wpf-low-level-keyboard-hook-sample.aspx

There is very important comment there that says Keyboard.Modifier property does not return correct values. First, I wanted to avoid linking to System.Windows.Form assembly and didn't pay much attention go this comments. Well, it didn't work. I had come back to using System.Windows.Forms.Control.ModifierKeys. To make interface more WPF compatible I just converted value this way:

System.Windows.Forms.Keys m = System.Windows.Forms.Control.ModifierKeys;
ModifierKeys m2 = ModifierKeys.None;
if ((m & System.Windows.Forms.Keys.Control) != 0)
m2 = m2 | ModifierKeys.Control;
if ((m & System.Windows.Forms.Keys.Alt) != 0)
m2 = m2 | ModifierKeys.Alt;
if ((m & System.Windows.Forms.Keys.Shift) != 0)
m2 = m2 | ModifierKeys.Shift;
if ((m & System.Windows.Forms.Keys.Apps) != 0)
m2 = m2 | ModifierKeys.Windows;

Wednesday, August 20, 2008

SystemEvents.PowerModeChanged Event and services

As I posted before I wrote small application to know how much time my daughter logged on computer. To do this I used SENS events. And then I noticed that numbers are to big. And the problem was that these events did not trigger when computer went to sleep or hibernate modes. And service did not stop either. .NET has SystemEvents.PowerModeChanged event that I decided to use. Surprise, it doesn't work from service. The solution was found in System.Management name space and ManagementEventWatcher class.

Here is the fragment of the code I used:



WqlEventQuery query = new WqlEventQuery("Win32_PowerManagementEvent");
_watcher = new ManagementEventWatcher(query);
_watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
_watcher.Start();


when my service started.

_watcher.Stop();

when it stopped.



void watcher_EventArrived(object sender, EventArrivedEventArgs e)
{
try
{
int eventType = Convert.ToInt32(e.NewEvent.Properties["EventType"].Value);
switch (eventType)
{
case 4:
Sleep();
break;
case 7:
Resume();
break;
}
}
catch (Exception ex)
{
Log(ex.Message);
}
}


- event handler.

Sunday, August 17, 2008

InkEdit does not work when there are no recognition package for your language

It was a midnight when I got pinged by my boss from Mexico. The Ink control that we inserted into our application didn't work. And we used InkEdit.

It was tested before, everything worked fine, there were no recent changes that could break it, and I didn't have Tablet PC at home to duplicate it. We had terminal session and I just couldn't get it. It worked before.

Finally, we realized that standard pop up control (with virtual keyboard) didn't work either. And then we noticed that current language was Spanish, and then we switched to English and everything worked.

So, what they (customer) did - they changed default keyboard input to Spanish, and InkEdit stopped working. Which makes some sense, InkEdit supposed to be used to recognize what you entered, not just store pictures. If you need just store everything was scrubbled without attempting to recognize it, InkPicture should be used.

Getting File size using ContentLength when downloading from FTP server

There is a property ContentLength of class FtpWebResponse. When I wrote the code similar to example, provided by MSDN, this property always returned -1. As it turned out, it was important to read fine print. I had to set method to GetFileSize to get proper value. So, what I got in result that worked was this:

// Get file size
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(_uri);
request.Method = WebRequestMethods.Ftp.GetFileSize;
request.Credentials = credential;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
long fileSize = response.ContentLength;

// Download file
request = (FtpWebRequest)WebRequest.Create(_uri);
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = credential;
response = (FtpWebResponse)request.GetResponse();


And then getting stream from response etc.

Saturday, August 09, 2008

How to implement LinkControl in WPF

There is no LinkControl in WPF, so if you need to have hyperlink in your form, different approach is needed. I used this:

XAML:

<TextBlock>Go<Hyperlink NavigateUri="http://counttime.alexeyev.org" Click="Hyperlink_Click">http://counttime.alexeyev.org</Hyperlink></TextBlock>

C#:

private void Hyperlink_Click(object sender, RoutedEventArgs e)

{
Hyperlink link = (Hyperlink)sender;
Process.Start(link.NavigateUri.AbsoluteUri);
}

Thursday, August 07, 2008

InkEdit recognize in WinForms

In our recent project we used InkEdit control, and we did it in WinForms. Doing that found there are some tricky things there.

Here is the problem. Let's say I need to close modal window with this control. Before doing that I would like to call Recognize to get latest data from control. However, the procedure is not synchronized. It works this way.

Thread A: call to method recognize returns immediately.
Thread B: event Recognition is raised, if there are data to recognize.

So, I created EventWaitHandle object in thread A and set signal in thread B. Then I waited for WaitHanlde for up to 1 second. If I got event signal - there was text to get from control and if I didn't, there were just nothing new to recognize.

However, it didn't work. And the reason it didn't work was that for data to get into control, it had to get some time in main thread, which is thread A. So, I just got kind of deadlock here.

Unfortunately, I couldn't resolve the problem without call to Application.DoEvents(), which I always try to avoid.

Final Result.

Thread A:
Recognize with WaitHandle
Wait for WaitHandle signal.
If signal received - call ApplicationDoEvents.
Get value from control.

Thread B:
Event handler for Recognition set signal for EventWaitHandle.

Thursday, May 08, 2008

Access to remote pipes in Vista

I continue experimenting with Named Pipes. I had Pipe Server installed at Windows XP and client running under Windows Vista. Everything worked fined. But then I changed it and run Server on Vista and client on XP. Suddenly I got message "Logon failure: unknown user name or bad password."

Apparently, I didn't enable file sharing on Vista computer. So, to use pipes remotely you have to enable file sharing.

Wednesday, April 30, 2008

Using Control.Dock in WinForms does not work as it should.

Simple rule, if it's not Dock.Full, use panels as containers. Otherwise it's unpredictable when it works, and when not.

Sunday, April 27, 2008

How to browse for computer name

I never thought that a simple task of browsing for a computer name would be such a pain. Nevertheless it was.

What I need is to give user the dialog to choose computer from his local network.

First step:
WPF does not have FolderBrowser dialog. Well, we can use Windows.Forms for that. The have FolderBrowserDialog.

Step two:
In my dialog I wanted to start search from "Network". FolderBrowserDialog has RootFolder property for that. But enumeration does not support Network folder. I don't know why they removed it, because technically all they had to do is to add one enumeration value with proper value setting. So, that was the first obstacle.

Step three:
Doing some research I found class System.Windows.Forms.Design.FolderNameEditor from System.Design.dll. It's not perfect, but I could make it work easily and this one could start from folder Network. However, when I did that the return value was empty string.

Step four:
I started to research again. Now I found that Win32 method SHBrowseForFolder that is actually called behind the scene, returns computer name in very weird way. It retuns it not where directory path is usually returned, but in field name displayname. And that, for sure, was not handled properly in this System.Windows.Forms.Design.FolderNameEditor class.

Step five:
I researched some more and found a component ShellFolderBrowser at CodeProject. Now guess what? It didn't have RootFolder. So, I was where I started from.

Step six:
I decided that I researched enough. At this point it become faster for me to just write some code. Since there are plenty of classes that can support folder browsing in general, I decided not to write "universal" component or something like that. I just wrote simple method to choose computer name. It was based on ShellFolderBrowser I found at CodeProject (Author: Rama Krishna Vavilala) and some rip off from System.Windows.Forms.Design.FolderNameEditor extracted using Reflector.

It's draft, simple and not pretending to be component for general use. However, if you are interested, you can download the code from here ComputerBrowserZip. Again, you can learn more from looking at .NET code by Reflector or to ShellFolderBrowser.

Thursday, April 10, 2008

BindingComplete, Exception and DevExpress TextEdit

In our current application we are using CSLA and DevExpress.

While CSLA provides validation rules, they allow to continue data modification even when some property has incorrect value. What I wanted to achieve was little bit different. I wanted to not allow assign incorrect values. The logical approach for me was to throw exception in property setter. But then it took me sometime to figure out how to handle this exception.

DevExpress TextEdit just return "Invalid value" as ErrorText. The exception I raised was just lost. After researching he subject I finally understood how it works.

1. When data assigned to control, it calls OnValidating method.
2. The method puts data to the object through binding
3. Exception raised in property setter
4. This exception is caught by Binding object itself
5. If FormattingEnabled property of Binding object is set to true, then exception is not raised again. Instead, BindingComplete event is raised
6. Cancel is set to true and that value returned to OnValidating
7. Here is some specifics for TextEdit (or BaseEdit for that matters), it has InvalidValue event, but there is no information about exception available, it just puts "Invalid Value" to ErrorText.

So, final solution for me was
1. Since I use BindingSource object, I handle BindingSouce.BindingComplete event with this code


public virtual void bindingSource_BindingComplete(object sender, BindingCompleteEventArgs e)
{
if (e.BindingCompleteState == BindingCompleteState.Exception)
{
MessageBox.Show(MainForm.MainForm, e.ErrorText, Properties.Messages.labelError, MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Binding.ReadValue();
}
}

Than for InvalidValue event I have


private void BaseEditInvalidValueHandler(object sender, DevExpress.XtraEditors.Controls.InvalidValueExceptionEventArgs e)
{
if ((e.Exception is WarningException) && (e.Exception.InnerException == null))
e.ExceptionMode = DevExpress.XtraEditors.Controls.ExceptionMode.NoAction;
else
e.ExceptionMode = DevExpress.XtraEditors.Controls.ExceptionMode.ThrowException;
}



And of cause, there is topic at MSDN for that, http://msdn2.microsoft.com/en-us/library/k26k86tb.aspx

Update:
BindingSourceRefresh component from CSLA handles refreshing in BindingComplete event

Tuesday, April 01, 2008

Aliases work in order clause in MS-SQL 2005

Not that I solved any problem but, today I accidentally found that aliases work in order clause if not used in functions. For example, this will work.

select c.Name, l.Value Lookup
from Customer c
left outer join LookupValue l on l.id = c.LookupId
order by Lookup

This didn't work in Oracle four years ago, where l.Value had to be used instead. And this is really nice.

Monday, March 24, 2008

Emulating key pressing in .NET

I knew it should have been there. But somehow it didn't pop up in search no matter how I tried to find how to emulate key press in .NET.

So, to emulate key pressing in .NET just use System.Windows.Forms.SendKeys class. As simple as that.

Tuesday, February 26, 2008

Alter column set default doesn't work in T-SQL 2005

Looking to the MSDN help http://msdn2.microsoft.com/en-us/library/ms174123(SQL.100).aspx (Same topic I have in local MSDN Library) I could not understand why following commands did not work:

alter table [Albums] alter column [Comment] set default 'default';
alter table [Albums] alter column [Comment] set not null;

I was getting error message:

Incorrect syntax near the keyword 'set'.

First I found this solution for default:

alter table [Albums] add constraint DF_Albumns_Comment default 'default' for [Comment];

constraint name is optional;

For not null similar solution:

alter table [Albums] add constraint NN_Albumns_Comment check ([Comment] is not null);

produces slightly different result. This new constraint is added over existing table definitions. The definition for column [Comment] is that it still allows nulls.

But then I noticed that online help was in fact for SQL 2008 compact edition, and later I found updated version (February 2008) for SQL 2005
http://msdn2.microsoft.com/en-us/library/ms190273.aspx

Now ther is no option for default here, so solution with new constraint should be used. But for not null constraint I can use this:

alter table [Albums] alter column [Comment] varchar(30) not null;

And no mistery.

Monday, February 18, 2008

SENS events

I decided to write very small program that would calculate how much time someone spends at computer. Not too sophisticated, just based on login-logout time and screen saver. Researching the subject I found that this days it should use System Event Notification Services (SENS) to be able to work on Windows Vista. And then I had very hard time figuring out how to make it work based on MSDN information.

What helped me is this article, particularly example.

http://dotnet.sys-con.com/read/105651_1.htm

The program is not written, but link is still good. And I tried example, it works.

Thursday, January 17, 2008

Application.ThreadException Event is static

Today I had problems with handling exceptions in new application. Instead of showing message my application just terminated, and I couldn't find where.

The reason was that Application.ThreadException Event is static event, but handler I assigned to it was not static method. I didn't get any warnings, it just didn't work.

Extracting Date from DateTime in MS SQL

I don't know may be you know better way of extracting Date from DateTime in MS SQL, but the one I like is

DATEADD(d, 0, DATEDIFF(d, 0, [DateTime Column]))

Using conversion would be converting datetime to number, then to integer, then back to date. I didn't measure performance, but don't see much problems with arithmetic.