Guy

Blog about .Net, architechture and Oracle

WPF Scrolling

WPF Scrolling

In WPF you get scrolling support by wrapping the content control you want to add scrolling to with the ScrollViewer content control.

<ScrollViewer>
    <TextBlock LineHeight="20" Grid.Row="0">
    </TextBlock>
</ScrollViewer>

It adds a disabled scroll which changes to enabled if the content has overflowed the size of it’s container.

image

 

 

You may use the VerticalScrollBarVisibility="Auto" property of the ScrollViewer if you wish the scrollbar to appear only when enabled (it’s default state is Visible) and use HorizontalScrollBarVisibility="Visible" when the horizontal scrollbar is what you need.

ScrollViewer class holds several methods that allow you to programmatically scroll the content.

  • LineUp() / LineDown() / LineLeft() / LineRight() -  Moves the scrollbar once up/down/left/right.
  • PageUp() / PageDown() / PageLeft() / PageRight() -  Moves the scrollbar a full page up/down/left/right.
  • ScrollToHome – scrolls to the top of the scrollable container.
  • ScrollToEnd – scrolls to the bottom of the scrollable container.
  • ScrollToVerticalOffset / ScrollToHorizontalOffset – scrolls to a specific position.

For example, scrolling to a specific position:
double _pos;
double.TryParse(PositionNum.Text,out _pos);
DemoScroller.ScrollToVerticalOffset(_pos);

You may download the demo code here.

Read Pro WPF in C# 2008 by Matthew Macdonald for more WPF stuff.

Posted: Jul 06 2009, 01:08 PM by Guy Shvoron | with no comments
תגים:, ,

WPF Grid Shared Size Groups

WPF Grid Shared Size Groups

The Grid rows and columns are given a direct size by the developer or are automatically sized by their content’s size. By using shared sized group feature you may also set the size of the rows or columns by the size of a different row/column at the current grid or from a different grid available in the page.

For example, you may set the column width of column 2 by the column width of column one at the same grid. You may also set the column width of column 2 at grid 2 by column 1 at grid 1.

Normal Grid:

image

column 2 width set by column 1 width of the same grid:

image

 

Column 2 width at grid 2 is set by column 1 width of grid 1:

image

 

 

You may reuse the shared size group in multiple columns or rows at multiple grids.
Please pay attention to the fact that the share size group is set both ways, that means that the size of all shared column (or rows) is set by the largest column (or row).

Sharing a group can be done by simply setting the ColumnDefinition’s SharedSizeGroup property to a specific name that will be also used at the other’s column’s definition.

<Grid Grid.Row="0"Background="AliceBlue"  ShowGridLines="True">
    <
Grid.ColumnDefinitions>
        <
ColumnDefinition Width="Auto"SharedSizeGroup="ShareThis"></ColumnDefinition>
        <
ColumnDefinition Width="Auto"></ColumnDefinition>
        <
ColumnDefinition></ColumnDefinition>
    </
Grid.ColumnDefinitions>

….

<Grid Grid.Row="2" Background="AntiqueWhite"  ShowGridLines="True">
    <
Grid.ColumnDefinitions>
        <
ColumnDefinition Width="Auto" SharedSizeGroup="ShareThis"></ColumnDefinition>
        <
ColumnDefinition Width="Auto" ></ColumnDefinition>
        <
ColumnDefinition></ColumnDefinition>
    </
Grid.ColumnDefinitions>

One last thing to do is to set the scope of the shared size group in the current window, this can be done by setting Grid.IsSharedSizeScope property to true at a container whose scope encompasses the shared columns/rows.

<Grid Grid.IsSharedSizeScope="True" >

You can view a sample code here.

Posted: Jun 30 2009, 08:54 AM by Guy Shvoron | with no comments
תגים:, ,

XAML special characters and white spaces

XAML special characters and white spaces

Some beginner stuff i found a lot of people do not know about.

XAML is based upon the rules of XML. What this means is that characters such as ‘&’, ‘<’, ‘>’ and ‘”’ have their own meaning in XML and the XAML parser will understand them differently then what you intended them for.
If you wish your button’s content to look like this <I am a Button> then you must change your XAML text to  <Button Content="&lt;I am a Button&gt;"></Button>  which creates a button like this:

image

Other character replacements are (don't forget the ‘;’) :

  • &amp; that replaces ‘&’.
  • &quot; that replaces ‘”’.

You also need to pay attention to white spaces. XML, by default, collapses all white spaces, carriage returns and tabs into a single space character if they appear between text and will eliminate the spaces completely before or after the text.

For example, the following XAML:
<TextBox>
carriage return    space         and some more "      ".
   
</TextBox>

will be shown as:

image

To fix this, we need to use the xml:space="preserve" which is set in the element level of the control like this:
<TextBox xml:space="preserve">
carriage return    space         and some more "      ".
   
</TextBox>

Now our text box looks like this:

image

Posted: Jun 24 2009, 09:13 AM by Guy Shvoron | with no comments
תגים:, ,

Oracle SQL performance booster

Oracle SQL performance booster

I want to share with you one of the best performance boosters i had come across.

In most of the applications i was working on (in the past and present) we did not use datasets, but built our own custom objects. The data access layer usually consisted of calls to stored procedures, functions and code generated SQLs all going through the MS data access application block.

Calling stored procedures and functions was easy enough, we used the generated SQL only when we needed to insert large amount of data (fields) which was easier done through generated SQL.

At some of the business logic code we needed to insert data to one table, then insert different data to a second table and update data at a third table.
This can be done using 3 separated calls to the database wrapped with a transaction (all done in code).

There is a different and much better solution that calls the database only once for all three SQL statements and wraps it with an automatic transaction.

This can be done by wrapping the 3 SQL statements with a Begin and End.

For example:

BEGIN
//statement one
insert into
is_me_main
  (
id, drivertype, girteid, isserverok, errordescription, errorname, errorcode,
       servicename, occurtime)
values
 
(v_id, v_drivertype, v_girteid, v_isserverok, v_errordescription, v_errorname,
         v_errorcode, v_servicename, v_occurtime);


//statement two        
insert into is_me_services
  (
id, driver_id, service_name, is_active, priority, girtes)
values
 
(v_id, v_driver_id, v_service_name, v_is_active, v_priority, v_girtes);


//statement three 

update is_me_lanes
  
set id = v_id,
       lineid = v_lineid,
       isok = v_isok,
       errordescription = v_errordescription,
       errorname = v_errorname,
       errorcode = v_errorcode,
       occurtime = v_occurtime
where id = v_id
  
and laneid = v_laneid;
END;

Wrap all this with a string variable and make the call to the database:

Database db = DatabaseFactory.CreateDatabase();

using (DbCommand dbCommand = db.GetSqlStringCommand(sql))
{
    Logger.WriteToLog(dbCommand);
    db.ExecuteNonQuery(dbCommand);
}

Beyond the performance boost that this solution gives you when you only create a single call to the database instead of three, it also acts as a single transaction and if one statement fails an automatic rollback occurs.

If you look even more closely at this example, you may realize that this can be expanded towards building whole SQL generated stored procedures on the fly.

Scrolling Text animation in WPF

Scrolling Text animation in WPF

In one of our WPF applications we came across the need to present static data that is larger then it’s container (grid/text block/panel). We could have chosen to reduce the font size, but this was inappropriate for our situation because the application was intended to appear on a LCD screen and be seen from as far as possible.

We decided to present the text inside a text block and scroll it (bottom to top) if it became too large for the text block.

After quite a few trail and error attempts we succeeded in creating the required scrolling text.

We started by adding a RenderTransform behavior to the text block that’s showing the scrollable text. Using RenderTransform you may take the control and move it, resize it, rotate it and so on.


<TextBlock Name="RollText" Grid.Row="1" Foreground="White" TextAlignment="Center">
    <
TextBlock.RenderTransform>
        <
TranslateTransform x:Name="translate" />
    </
TextBlock.RenderTransform>
</
TextBlock>

After adding the text to the text block, the scrolling animation can be done by adding DoubleAnimation to a StoryBoard.
We create the DoubleAnimation object and set where we wish the animation to begin and where it ends together with the animation duration.

animation.From = RollText.ActualHeight;
animation.To = RollText.ActualHeight * -1;
animation.Duration = TimeSpan.FromSeconds(10);

Next we set the target element which in our case is the translate transform of the text block.

Storyboard.SetTargetName(animation, "translate");

Now we set the target dependency property that will be changed by the animation and add the animation to the storyboard. The dependency property we set as target is the “Y” position of the target element selected above, this will enable the text block to move on the Y axis from bottom to top.

PropertyPath propPath = new PropertyPath("Y");
Storyboard.SetTargetProperty(animation, propPath);
storyboard.Children.Add(animation);

The last thing left to do is to run the storyboard.

storyboard.RepeatBehavior = RepeatBehavior.Forever;
storyboard.Begin(RollText, true);

You can download the code here.

3D WPF samples by Josh Smith

Look here at these awesome 3D WPF samples.

Josh Smith shows what you need to add cool 3D abilities to you application.

Posted: Jun 15 2009, 09:07 AM by Guy Shvoron | with no comments
תגים:, ,

How to fetch remote session’s information on a terminal server

How to fetch remote session’s information on a terminal server

First a confession, i did not plan to start my first posts writing about TCP,Terminal Servers and their likes, but once i begun, i realized there is so much information i gathered that i just must share.

In this post i will show how to fetch additional information about the session the current process is running under in a terminal server. It is mainly based upon a previous post “Using terminal services API to determine if the application is being run remotely”.

The code:

We first need to to get the terminal server’s session information which is returned inside a buffer variable.

This is how it is done:
hRet = WTSQuerySessionInformation(serverHandle, (IntPtr)SessionID,
infoClass, ref ppBuffer, ref iBytesReturned);

You can leave out (or set it an equivalent null value) serverHandle and SessionID if the information you request is about the current running process.
infoClass is an enum holding items representing the information you are able to fetch from the ppBuffer variable (see here for more information).

In my code sample i fetch information about:

  • WTSClientAddress – IP address of the client.
  • WTSClientName – Name of client machine.
  • WTSDomainName – Domain name of which the logged-on user belongs.
  • WTSUserName – The user name of the logged-on user.
  • WTSWinStationName – The name of the Terminal Services session.

Most items of the infoClass are extracted from the buffer in the same way:
sResult = Marshal.PtrToStringAuto(ppBuffer);

PtrToStringAuto Allocates a managed String and copies a specified number of characters from an unmanaged string into it (see here for more information).

Fetching WTSClientAddress is a little but trickier and is shown in the attached code.

You can find the code here.

The information that was fetched on one of our terminal servers when running the code sample is shown below:
ip2

 

How to find an available port in C#

How to find an available port in C#

As i discussed in my post Development considerations taken when installing client application on a terminal server, there will be times you will have to find an available port yourself, without relying on WCF to do that for you.

This can be achieved by locating all ports currently used in your machine and returning the first port number that is not being used.
The machine in question can hold open ports in different communication protocols and states(learn more about TCP and UDP):

  1. Active TCP connections – TCP connections that are currently active, opened and connected to a remote port.
  2. Active TCP listeners – port that was opened, ready to receive data but is not active (not connected to a remote port).
  3. Active UDP listeners – opened UDP port which is ready to accept incoming calls.

Ok, after we got all this out of the way, lets look at some code:

We will be using System.Net.NetworkInformation and System.Net.

The first thing we need to do is fetch 
IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
which provides information about the local computer's network connectivity.

Now that we got the IPGlobalProperties, we can query it for the opened ports.

We begin by fetching all the active TCP connections:
//getting active connections
           
TcpConnectionInformation[] connections = properties.GetActiveTcpConnections();

Adding the ports to an array that holds all the opened ports (pay attention to startingPort variable that defines the minimum available port number to search from – this is an optional feature):
List<int> portArray = new List<int>();
portArray.AddRange(from n in connections
                   where n.LocalEndPoint.Port >= startingPort
                   select n.LocalEndPoint.Port);

Next we fetch all active TCP listeners:
IPEndPoint[] endPoints;
//getting active tcp listners - WCF service listening in tcp
endPoints = properties.GetActiveTcpListeners();

Also adding the ports to the array:
portArray.AddRange(from n in endPoints
                   where n.Port >= startingPort
                   select n.Port);

Finally we fetch all active UDP listeners:
//getting active udp listeners
endPoints = properties.GetActiveUdpListeners();

portArray.AddRange(from n in endPoints
                   where n.Port >= startingPort
                   select n.Port);

All that is now left to do is to find the first port number that does not exist in the portArray beginning at the startingPort :

portArray.Sort();

for (int i = startingPort; i < UInt16.MaxValue; i++)
    if(!portArray.Contains(i))
        return i;

That's it, you can find the code here.

Using terminal services API to determine if the application is being run remotely

Using terminal services API to determine if the application is being run remotely

Imagine you have a client application that may be executed remotely or locally at the user’s computer. For most times, you will need to know whether the instance of the application is being run directly on a user’s desktop or from a remote client machine.

You will need to be aware of this for several reasons, some of them were mentioned in my previous post Development considerations taken when installing client application on a terminal server.

This can be achieved by playing a bit with terminal services API.

Code available here.

The work is basically done by calling two terminal services API functions:

int sessionInfo = WTSQuerySessionInformation(System.IntPtr.Zero, System.IntPtr.Zero,  WTS_INFO_CLASS.WTSSessionId, ref buffer, ref bytesReturned);

The WTSQuerySessionInformation function gets session information for the specific session on the terminal server (see link for more information).

The requested information is defined by WTS_INFO_CLASS. In this example we are looking for the session id so we set it to WTS_INFO_CLASS.WTSSessionId.

The other function called is:

int currentSessionId = WTSGetActiveConsoleSessionId();

The WTSGetActiveConsoleSessionId function retrieves the Terminal Services session that is currently attached to the physical console. The physical console is the monitor, keyboard, and mouse (see link for more information).

By comparing sessionInfo to currentSessionId we discover if the application is running locally or remotely. If sessionInfo equals to currentSessionId  then the application is running locally, otherwise, remotely.

If you don’t want to go through the trouble of the API, there is a simpler solution using SystemInformation.TerminalServerSession.
To use this you will need to add the System.Windows.Forms reference. Then, the only thing left to do is call SystemInformation.TerminalServerSession. This will return true if running on a terminal server and false if running locally.

Development considerations taken when installing client application on a terminal server

Development considerations taken when installing client application on a terminal server

Installing client applications on terminal server has become more and more common for a few years now.

It holds many benefits including:

  • Cutting down hardware costs (less hardware needed).
  • Only one installation is needed for numerous clients (the number of clients that can run on a terminal server is subject to the client’s resources consumption).
  • Better application maintenance.
  • and many more…

When installing client application on a terminal server we need to take the following considerations into account:

App.Config

The app.config file is now shared by many instances of the same program. If the file holds instance or user dedicated information, for example, styles selected by the user or special program behavior, you will have to move the data to a database, or to the user’s folder as an xml or other config file.

Saving Files Locally

In saving files locally you need to take into considerations that all of the program’s instances run from the same exe file located in the same path. Because of this you will have to save the files to the user specific folder at the user’s profile file at “Documents and Settings” folder or to any other specific folder defined at run time.

Logging

Out of the box (using log4net), all instances of the program will write to the same log which may be an undesirable outcome.
If your logging is done directly to a table at a database you may want to add a field indicating under which user the application is currently running and you will be able to filter the table rows by that user easily.
You are in a harder situation if you log directly to a file in the file system. The solution to this may be creating the log file under the user’s profile file at Documents and Settings folder.
In our applications we are using log4net which also gives us the ability to change to log file name at run time.
This can be done by the following example:

//This must be defined before the LogManager.GetLogger of log4net
log4net.GlobalContext.Properties["LogName"] = MyUserName;

Place the property in the app.config by setting the RollingFileAppender’s File like shown below:
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
  <
File type="log4net.Util.PatternString" value="Logs/%property{LogName}/log.log"/>
  <
param name="AppendToFile" value="true" /> 

WCF

When using WCF for cases you wish the client to serve also as a host for incoming tcp calls from the server, you will have to set the port dynamically (this can be done by leaving out the host’s port number in the app.config). In this case, the server does not have any knowledge of the port it needs to send the message to because the client’s host port was set dynamically.
I can think of at least two solutions for this:

First solution

Using WCF duplex communication (the client does not need to set a WCF host). In this solution, the client will call a published WCF method at the server. The method will receive client id, or ,IP and port and will keep it in memory along with the callback channel. When the time comes, the server will send the data back to the client using the callback channel found by the client id.

Sample Code:

The interface (implemented at the server):

[ServiceContract(CallbackContract=typeof(IMyCallback))]
public interface IMyDuplex
{
    [OperationContract(IsOneWay = true)]
    voidGetAsyncData(stringMachineID);
}

The callback interface (implemented at the client):

public interface IMyCallback
{
    [OperationContract(IsOneWay = true)]
    void GetAsyncData(MyObject data);
}

The implementation of IMyDuplex interface at the server that accepts the first call from the client and keeps the callback channel in memory for when it needs to send data to the client:

public static void GetAsyncData(string machineID)
{
    IMyCallback callback = OperationContext.Current.GetCallbackChannel<IMyCallback>();

    if(!string.IsNullOrEmpty(machineID))
    {
        if(!DuplexDictionary.ContainsKey(machineID))
            DuplexDictionary.Add(machineID, callback);
    }
}

Later, when it is time for the server to send the data to the client it fetches the callback from the DuplexDictionary by the client id and sends the data to the client:

DuplexDictionary[machineID].GetAsyncData(data);

Second solution

When Duplex communication is not possible, the client will have to fetch an available port and open a host at that port. It will send the server it’s IP and port that was dynamically created.
The server will open a proxy to the client by the IP and port that was sent to him and use it for any messages it may wish to send to the client.

Hi all

Hi all

 

My name is Guy Shvoron, sharing my knowledge with the world is very new to me and even a bit scary. It took a long time to decide to begin writing a blog mainly because of lack of free time (family, you know) but also because I did not think I had so much to say, but now I feel the time has come to begin sharing and learning because I gathered a lot of stuff to write about (free time is still an issue, but yeah, I can continue with this excuse forever…).

I am currently working on an exciting project holding the role of development manager/architect/developer with 5 very talented programmers and QA that make my job a lot easier. Hopefully i will be able to write about this project in the near future but this is pending management confirmation.

I will not only post what my team and i learned in the past years but also my life long experience at software development, management, architecture.NET, C# and Oracle.

In my first few posts i will write about the adventures i had writing applications that were installed on Terminal Servers, how we use .NET to connect to hardware devices, some Oracle tips, a lot of stuff about WPF, WCF and lots more.

Please feel free to comment, write and email because this platform is not only for me to publish my knowledge and thoughts but also a great tool that i can learn and improve by.

Special thanks to Maor David for the time, enthusiasm and knowledge which helped me start this journey.

I highly appreciate your interest and bon voyage.

Guy.