Locking is essential in programs with multi-threading. It restricts code from being executed by more than one thread at the same time. The lock statement gets the exclusive monitor for an object and ensures that no other thread can access the object until the lock is released.
The lock statement automatically generates exception safe code, and in fact it is a syntactic shortcut for a call to the methods Monitor.Enter(obj) and Monitor.Exit(obj) with a try/finally block. However, if the thread is aborted after the lock is acquired but before entering the try block, the lock will not be released. Also, bear in mind that the Monitor.Enter() waits to acquire the lock forever which may introduce a possible deadlock condition.
To avoid these issues with the lock statement, it is better either to use different overloads of the Monitor.Enter() call which take additional arguments, such as amount of time or a number of milliseconds to wait while trying to obtain a lock, or use the Monitor.TryEnter() call which lets you specify a timeout for an operation. But before you can use any of these workarounds, you need to convert the lock statement into its equivalent code. The Lock to Try/Finally refactoring can help you to automate this task.
The refactoring produces the following result once it is applied:
Now you can modify the code as required, for instance:
This code does not have issues as with the lock statement mentioned above.
TZONE
วันอาทิตย์ที่ 23 กันยายน พ.ศ. 2555
Refactorings for simplifying of .NET 4.0 parallel computing development
Many personal computers and workstations have two or four cores that enable multiple threads to be executed simultaneously. .Net Framework ver. 4.0 has been introduced a standardized and simplified way for creating robust, scalable and reliable multi-threaded applications. The parallel programming extension of .NET 4.0 allows the developer to create applications that exploit the power of multi-core and multi-processor computers. The flexible thread APIs of the extension are much simpler to use and more powerful than standard .NET threads.
The extension implements the concept of automatic dynamic parallelization of applications. It provides both ease-of-use and scalability in development of parallel programs. The concept is naturally integrated into .NET Framework by means of templatized classes (introduced in C# with generics) that encapsulate all low-level details such as threading, synchronization, scheduling, load balancing, etc., which makes the extension a powerful tool for implementing high-performance parallel applications.
Refactor! Pro provides several parallel computing refactorings that can help you to parallelize your code to distribute work across multiple processors. Here they are:
Show Visual Basic code… »
And the standard function that returns the list of primes in the specified limit:
Show Visual Basic code… »
Using the System.Diagnostics.Stopwatch class, we will count how much time it takes for finding all primes within the limit. The limit will be 300000:
Show Visual Basic code… »
The result of the standard code run is 18328 ms. Average CPU usage is 52%. Here’s the CPU usage history of the Intel(R) Core(TM)2 DUO CPU:
Now, let’s use the Convert to Parallel refactoring and improve the code:
The result of the Parallel.For code run is 9727 ms. Average CPU usage is 100%. CPU usage history:
Let’s change the Find method to use LINQ instead:
Show Visual Basic code… »
Result time: 19555 ms. Average CPU usage: 100%. CPU usage history:
Once again, using the same Convert to Parallel refactoring change the code:
Result time: 10111 ms. Average CPU usage: 100%. CPU usage history:
Now let’s use the Parallel.Invoke method. To use this method, we’ll split the limited number into 10 parts and make calculation in parallel. An additional helper method is needed in this case:
Show Visual Basic code… »
The standard code will look like this:
Show Visual Basic code… »
Result time without parallel optimization: 17816 ms. CPU usage: 52%. CPU usage history:
Now, use the Execute Statements in Parallel refactoring: The Parallel.Invoke method takes an array of delegates as an argument. The new code will look like this:
Show Visual Basic code… »
Result time: 10530 ms. CPU usage: 100%. CPU usage history:
The Execute Statements in Serial refactoring is an the opposite of the Execute Statements in Parallel, which moves all code from the Parallel.Invoke call and executes it as usual (serially).
Here’s the comparison chart of the different parallel methods used in comparison to the standard serial methods:
Time chart:
As you can see, using parallel computing almost doubles the speed of the code, even when the number of calculations is small. Parallel refactorings from Refactor! Pro make it easy to improve the speed of your code.
The extension implements the concept of automatic dynamic parallelization of applications. It provides both ease-of-use and scalability in development of parallel programs. The concept is naturally integrated into .NET Framework by means of templatized classes (introduced in C# with generics) that encapsulate all low-level details such as threading, synchronization, scheduling, load balancing, etc., which makes the extension a powerful tool for implementing high-performance parallel applications.
Refactor! Pro provides several parallel computing refactorings that can help you to parallelize your code to distribute work across multiple processors. Here they are:
- Convert to Parallel
Converts the code to run in parallel.
- Execute Statements in Parallel
Executes the selected independent statements in parallel.
- Execute Statements in Serial
Moves the child statements out from the Parallel.Invoke call and executes them serially.
Passes the appropriate BeginXXX and EndXXX methods (corresponding to this statement) to the FromAsync method of Task.Factory, launching an asynchronous operation and returning a handle to the associated Task.
Passes the current statement to the StartNew method of Task.Factory, launching an asynchronous operation and returning a handle to the associated Task.
All refactorings work in both CSharp and Visual Basic languages. In this article we will review the first three refactorings, and see how they help to speed-up the code in a real sample. There are several parallel programming methods we are going to use:- Parallel.For
- AsParallel
- Parallel.Invoke
- and a standard serial calculation method to compare with.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
| public class Prime { /// <summary> /// Determines whether the specified number is prime. /// </summary> /// <param name="number">The number.</param> /// <returns> /// true if the specified number is prime; otherwise, false. /// </returns> public static bool IsPrime( int number) { for ( int d = 2; d <= number / 2; d++) { if (number % d == 0) return false ; } return number > 1; } } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
| Public Class Prime ''' <summary> ''' Determines whether the specified number is prime. ''' </summary> ''' <param name="number">The number.</param> ''' <returns> ''' true if the specified number is prime; otherwise, false. ''' </returns> Public Shared Function IsPrime( ByVal number As Integer ) As Boolean For d As Integer = 2 To number / 2 If number Mod d = 0 Then Return False End If Next d Return number > 1 End Function End Class |
01
02
03
04
05
06
07
08
09
10
11
12
| static List Find( int limit) { var result = new List(); for ( int i = 2; i < limit; i++) { if (Prime.IsPrime(i)) result.Add(i); } return result; } |
01
02
03
04
05
06
07
08
09
10
11
| Shared Function Find( ByVal limit As Integer ) As List(Of Integer ) Dim result = New List(Of Integer )() For i As Integer = 2 To limit If Prime.IsPrime(i) Then result.Add(i) End If Next i Return result End Function |
1
2
3
4
5
6
7
8
| static void Main( string [] args) { Stopwatch stopwatch = Stopwatch.StartNew(); Find(300000); stopwatch.Stop(); Console.WriteLine( "Time passed: " + stopwatch.ElapsedMilliseconds + " ms." ); } |
1
2
3
4
5
6
7
| Shared Sub Main() Dim stopwatch As Stopwatch = Stopwatch.StartNew() Find(300000) stopwatch. Stop () Console.WriteLine( "Time passed: " + _ stopwatch.ElapsedMilliseconds + " ms." ) End Sub |
Now, let’s use the Convert to Parallel refactoring and improve the code:
The result of the Parallel.For code run is 9727 ms. Average CPU usage is 100%. CPU usage history:
Let’s change the Find method to use LINQ instead:
1
2
3
4
5
6
7
| static List FindLINQ( int limit) { IEnumerable numbers = Enumerable.Range(2, limit - 1); return (from n in numbers where Prime.IsPrime(n) select n).ToList(); } |
1
2
3
4
5
6
| Shared Function FindLINQ( ByVal limit As Integer ) As List(Of Integer ) Dim numbers As IEnumerable(Of Integer ) = Enumerable.Range(2, limit - 1) Return (From n In numbers _ Where Prime.IsPrime(n) _ Select n).ToList() End Function |
Once again, using the same Convert to Parallel refactoring change the code:
Result time: 10111 ms. Average CPU usage: 100%. CPU usage history:
Now let’s use the Parallel.Invoke method. To use this method, we’ll split the limited number into 10 parts and make calculation in parallel. An additional helper method is needed in this case:
1
2
3
4
5
6
7
8
| static void CheckRange(List result, int n, int limit, int factor) { for ( int i = n * (limit / factor); i < (n + 1) * (limit / factor); i++) { if (Prime.IsPrime(i)) result.Add(i); } } |
1
2
3
4
5
6
7
| Shared Sub CheckRange( ByVal result As List(Of Integer ), ByVal n As Integer , ByVal limit As Integer , ByVal factor As Integer ) For i As Integer = n * (limit / factor) To (n + 1) * (limit / factor) If Prime.IsPrime(i) Then result.Add(i) End If Next i End Sub |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
| static List FindRange( int limit) { var result = new List(); CheckRange(result, 0, limit, 10); CheckRange(result, 1, limit, 10); CheckRange(result, 2, limit, 10); CheckRange(result, 3, limit, 10); CheckRange(result, 4, limit, 10); CheckRange(result, 5, limit, 10); CheckRange(result, 6, limit, 10); CheckRange(result, 7, limit, 10); CheckRange(result, 8, limit, 10); CheckRange(result, 9, limit, 10); return result; } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
| Shared Function FindRange( ByVal limit As Integer ) As List(Of Integer ) Dim result = New List(Of Integer )() CheckRange(result, 0, limit, 10) CheckRange(result, 1, limit, 10) CheckRange(result, 2, limit, 10) CheckRange(result, 3, limit, 10) CheckRange(result, 4, limit, 10) CheckRange(result, 5, limit, 10) CheckRange(result, 6, limit, 10) CheckRange(result, 7, limit, 10) CheckRange(result, 8, limit, 10) CheckRange(result, 9, limit, 10) Return result End Function |
Now, use the Execute Statements in Parallel refactoring: The Parallel.Invoke method takes an array of delegates as an argument. The new code will look like this:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
| static List FindRangeParallel( int limit) { var result = new List(); Parallel.Invoke( () => CheckRange(result, 0, limit, 10), () => CheckRange(result, 1, limit, 10), () => CheckRange(result, 2, limit, 10), () => CheckRange(result, 3, limit, 10), () => CheckRange(result, 4, limit, 10), () => CheckRange(result, 5, limit, 10), () => CheckRange(result, 6, limit, 10), () => CheckRange(result, 7, limit, 10), () => CheckRange(result, 8, limit, 10), () => CheckRange(result, 9, limit, 10)); return result; } |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
| Shared Function FindRangeParallel( ByVal limit As Integer ) As List(Of Integer ) Dim result = New List(Of Integer )() Parallel.Invoke( Sub () CheckRange(result, 0, limit, 10), Sub () CheckRange(result, 1, limit, 10), Sub () CheckRange(result, 2, limit, 10), Sub () CheckRange(result, 3, limit, 10), Sub () CheckRange(result, 4, limit, 10), Sub () CheckRange(result, 5, limit, 10), Sub () CheckRange(result, 6, limit, 10), Sub () CheckRange(result, 7, limit, 10), Sub () CheckRange(result, 8, limit, 10), Sub () CheckRange(result, 9, limit, 10)) Return result End Function |
The Execute Statements in Serial refactoring is an the opposite of the Execute Statements in Parallel, which moves all code from the Parallel.Invoke call and executes it as usual (serially).
Here’s the comparison chart of the different parallel methods used in comparison to the standard serial methods:
Time chart:
As you can see, using parallel computing almost doubles the speed of the code, even when the number of calculations is small. Parallel refactorings from Refactor! Pro make it easy to improve the speed of your code.
Refactorings to execute statements asynchronously
Refactorings to execute statements asynchronously
In continuing with the ‘Refactorings for simplifying of the .NET 4.0 parallel computing development‘ thread, let’s review the additional refactoring for executing statements asynchronously called in the same manner – Execute Statement Asynchronously. The refactoring is available in two versions:
The TaskFactory simplifies creation and startup of simple background tasks dramatically. For example, consider that we have a Stream and a byte buffer, and we want to read from that stream into the buffer. Synchronously, we could do the following:
The Execute Statement Asynchronously (FromAsync) refactoring will allow us to convert this code into the FromAsync call:
resulting in the following one:
The FromAsync call returns a handle to the associated Task. The resulting Task object will, by default, be executed on a thread pool thread. Combine this support with the ability to do Task.WaitAll, Task.WaitAny, Task.Factory.ContinueWhenAll, and Task.Factory.ContinueWhenAny, and we can achieve a very useful synchronization functionality having just a little code.
The second version of the refactoring (StartNew) launches an asynchronous operation by passing the current statement to the StartNew method of Task.Factory. For instance, using StartNew is more efficient than creating and starting tasks manually, because the StartNew method not only starts a new task but also applies the required synchronization to the process. If you construct a task using the Task’s constructor, you then pay this synchronization cost when calling the Start method, because you need to have protection against the chance that another thread is concurrently calling Start. However, if we use the Task.Factory.StartNew, we know that the task will have already been scheduled and started by the time we get the Task reference returned, which means that it is no longer possible for threads to race to call Start, because every call to Start will fail. In the case of using StartNew, we can avoid the additional synchronization and take a faster and easier approach for scheduling a Task.
The Execute Statement Asynchronously (StartNew) refactoring can be applied on any statement:
which will result in:
The difference between the StartNew and FromAsync methods is that using the latter for the things like Stream (i.e., when API offers the appropriate BeginXxx and EndXxx methods), you will actually end up using the async I/O underneath the covers, e.g., I/O Completion Ports in Windows which provide an efficient threading model for processing multiple asynchronous I/O requests on a multiprocessor system. The FromAsync method in this case provides the best way to achieve I/O scalability, and is more efficient than blocking multiple CPU threads doing a synchronous operation using the StartNew method.
—–
Products: CodeRush Pro
Versions: 12.1 and up
VS IDEs: 2008 and up
Updated: Sep/17/2012
ID: R069
In continuing with the ‘Refactorings for simplifying of the .NET 4.0 parallel computing development‘ thread, let’s review the additional refactoring for executing statements asynchronously called in the same manner – Execute Statement Asynchronously. The refactoring is available in two versions:
- Execute Statement Asynchronously (FromAsync)
- Execute Statement Asynchronously (StartNew)
The TaskFactory simplifies creation and startup of simple background tasks dramatically. For example, consider that we have a Stream and a byte buffer, and we want to read from that stream into the buffer. Synchronously, we could do the following:
The Execute Statement Asynchronously (FromAsync) refactoring will allow us to convert this code into the FromAsync call:
resulting in the following one:
The FromAsync call returns a handle to the associated Task. The resulting Task object will, by default, be executed on a thread pool thread. Combine this support with the ability to do Task.WaitAll, Task.WaitAny, Task.Factory.ContinueWhenAll, and Task.Factory.ContinueWhenAny, and we can achieve a very useful synchronization functionality having just a little code.
The second version of the refactoring (StartNew) launches an asynchronous operation by passing the current statement to the StartNew method of Task.Factory. For instance, using StartNew is more efficient than creating and starting tasks manually, because the StartNew method not only starts a new task but also applies the required synchronization to the process. If you construct a task using the Task’s constructor, you then pay this synchronization cost when calling the Start method, because you need to have protection against the chance that another thread is concurrently calling Start. However, if we use the Task.Factory.StartNew, we know that the task will have already been scheduled and started by the time we get the Task reference returned, which means that it is no longer possible for threads to race to call Start, because every call to Start will fail. In the case of using StartNew, we can avoid the additional synchronization and take a faster and easier approach for scheduling a Task.
The Execute Statement Asynchronously (StartNew) refactoring can be applied on any statement:
which will result in:
The difference between the StartNew and FromAsync methods is that using the latter for the things like Stream (i.e., when API offers the appropriate BeginXxx and EndXxx methods), you will actually end up using the async I/O underneath the covers, e.g., I/O Completion Ports in Windows which provide an efficient threading model for processing multiple asynchronous I/O requests on a multiprocessor system. The FromAsync method in this case provides the best way to achieve I/O scalability, and is more efficient than blocking multiple CPU threads doing a synchronous operation using the StartNew method.
—–
Products: CodeRush Pro
Versions: 12.1 and up
VS IDEs: 2008 and up
Updated: Sep/17/2012
ID: R069
Similar Posts:
วันจันทร์ที่ 10 กันยายน พ.ศ. 2555
Sort Data Using Code with the Silverlight CollectionViewSource
Sort Data Using Code with the Silverlight CollectionViewSource
As a follow-up to last week's blog posting and newsletter, this week I am going to show you how to use the CollectionViewSource object in Silverlight to sort data using code. Sometimes you need something a little more flexible, so you will need to resort to writing some code. You can take advantage of the CollectionViewSource from your XAML, but dynamically change the sort order of your lists with just a few lines of code.
For this example, I will be using a simple Product class with three properties, and a collection of Product objects using the Generic List class. Try this out by creating a Product class as shown in the following code:
public class Product
{
public Product(int id, string name, string type)
{
ProductId = id;
ProductName = name;
ProductType = type;
}
{
public Product(int id, string name, string type)
{
ProductId = id;
ProductName = name;
ProductType = type;
}
public int ProductId { get; set; }
public string ProductName { get; set; }
public string ProductType { get; set; }
}
public string ProductName { get; set; }
public string ProductType { get; set; }
}
Create a collection class that initializes a property called DataCollection with some sample data as shown in the code below:
public class Products : List<Product>
{
public Products()
{
InitCollection();
}
{
public Products()
{
InitCollection();
}
public List<Product> DataCollection { get; set; }
List<Product> InitCollection()
{
DataCollection = new List<Product>();
{
DataCollection = new List<Product>();
DataCollection.Add(new Product(3, "PDSA Framework", "Product"));
DataCollection.Add(new Product(1, "Haystack", "Product"));
DataCollection.Add(new Product(2, "Fundamentals of .NET eBook", "Book"));
DataCollection.Add(new Product(1, "Haystack", "Product"));
DataCollection.Add(new Product(2, "Fundamentals of .NET eBook", "Book"));
return DataCollection;
}
}
}
}
The screen shot shown in Figure 1 is a Silverlight page that allows the user to sort the Product data by either the Product Name or the Product Type.
Figure 1: Sorting data using code in Silverlight
Notice that the data added to the collection is not in any particular order. Create a Silverlight page and add two XML namespaces to the UserControl.
xmlns:scm="clr-namespace:System.ComponentModel;assembly=System.Windows"
xmlns:local="clr-namespace:SLSortData"
xmlns:local="clr-namespace:SLSortData"
The 'local' namespace is the name of the project that you created. The 'scm' namespace references the System.Windows.dll and is needed for the SortDescription class that you will use for sorting the data. Create a UserControl.Resources section in your Silverlight page that looks like the following:
<UserControl.Resources>
<local:Products x:Key="products" />
<CollectionViewSource x:Key="prodCollection"
Source="{Binding Source={StaticResource products},
Path=DataCollection}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="ProductName"
Direction="Ascending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</UserControl.Resources>
<local:Products x:Key="products" />
<CollectionViewSource x:Key="prodCollection"
Source="{Binding Source={StaticResource products},
Path=DataCollection}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="ProductName"
Direction="Ascending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</UserControl.Resources>
The first line of code in the resources section creates an instance of your Products class. The constructor of the Products class calls the InitCollection method which creates three Product objects and adds them to the DataCollection property of the Products class. Once the Products object is instantiated you now add a CollectionViewSource object in XAML using the Products object as the source of the data to this collection. A CollectionViewSource has a SortDescriptions collection that allows you to specify a set of SortDescription objects. Each object can set a PropertyName and a Direction property. As you see in the above code you set the PropertyName equal to the ProductName property of the Product object and tell it to sort in an Ascending direction.
The two Radio Buttons on the page are created using the following xaml:
<RadioButton Name="rdoSortName"
Tag="ProductName"
Checked="SortTheData"
Content="Sort by Name" />
<RadioButton Name="rdoSortType"
Tag="ProductType"
Checked="SortTheData"
Content="Sort by Type" />
Tag="ProductName"
Checked="SortTheData"
Content="Sort by Name" />
<RadioButton Name="rdoSortType"
Tag="ProductType"
Checked="SortTheData"
Content="Sort by Type" />
Notice the Tag attribute has been set with the name of the property that you want to sort on. The Checked attribute is set to an event procedure called SortTheData. This event procedure is shown in the following code.
private void SortTheData(object sender, RoutedEventArgs e)
{
if (lstData != null)
{
ICollectionView dataView;
{
if (lstData != null)
{
ICollectionView dataView;
dataView = (ICollectionView)lstData.ItemsSource;
dataView.SortDescriptions.Clear();
dataView.SortDescriptions.Add(
new SortDescription(((RadioButton)sender).Tag.ToString(),
ListSortDirection.Ascending));
dataView.SortDescriptions.Add(
new SortDescription(((RadioButton)sender).Tag.ToString(),
ListSortDirection.Ascending));
lstData.ItemsSource = dataView;
}
}
}
}
In this code you are retrieving the CollectionViewSource data from the ItemsSource property of the list box. You cast this as a ICollectionView object. Clear any existing SortDescriptions and then add a new SortDescription object to the SortDescriptions collection on the CollectionView. You pass to the constructor of the SortDescription class the Tag property of the Radio button that was selected. Remember this is the name of the property that you wish to sort on. The second parameter passed to the constructor is the direction of the sort, either Ascending or Descending.
That's all there is to it. A simple way to allow your users to sort on different properties with just a few lines of code!
NOTE: You can download the complete sample code (in both VB and C#) at my website. http://www.pdsa.com/downloads. Choose Tips & Tricks, then "Sort Data Using Silverlight CollectionViewSource" from the drop-down.
วันอังคารที่ 24 กรกฎาคม พ.ศ. 2555
Custom tool warning: Cannot import wsdl:portType
I created a WCF service library project in my solution, and have service references to this. I use the services from a class library, so I have references from my WPF application project in addition to the class library. Services are set up straight forward - only changed to get async service functions.
Everything was working fine - until I wanted to update my service references. It failed, so I eventually rolled back and retried, but it failed even then! So - updating the service references fails without doing any changes to it. Why?!
The error I get is this one:
Custom tool error: Failed to generate code for the service reference 'MyServiceReference'. Please check other error and warning messages for details.
The warning gives more information:
Custom tool warning: Cannot import wsdl:portType Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter Error: List of referenced types contains more than one type with data contract name 'Patient' in namespace 'http://schemas.datacontract.org/2004/07/MyApp.Model'. Need to exclude all but one of the following types. Only matching types can be valid references: "MyApp.Dashboard.MyServiceReference.Patient, Medski.Dashboard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" (matching) "MyApp.Model.Patient, MyApp.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" (matching) XPath to Error Source: //wsdl:definitions[@targetNamespace='http://tempuri.org/']/wsdl:portType[@name='ISomeService']
There are two similar warnings too saying:
Custom tool warning: Cannot import wsdl:binding Detail: There was an error importing a wsdl:portType that the wsdl:binding is dependent on. XPath to wsdl:portType: //wsdl:definitions[@targetNamespace='http://tempuri.org/']/wsdl:portType[@name='ISomeService'] XPath to Error Source: //wsdl:definitions[@targetNamespace='http://tempuri.org/']/wsdl:binding[@name='WSHttpBinding_ISomeService']
And the same for:
Custom tool warning: Cannot import wsdl:port .. When you add a service reference, there are two ways the types that are used by the service can be handled: •The types are stored in a dll, and that dll is referenced from both the client and the server application. •The types are not in a dll referenced by the client. In that case the tool that creates the service reference, will create the types in the references.cs file. |
วันอังคารที่ 10 กรกฎาคม พ.ศ. 2555
ShowLoadingPanel
<devx:GridControl
Grid.Row="1" x:Name="dgTest"
ShowLoadingPanel="{Binding Path=IsLoading, Mode=TwoWay}"
ItemsSource="{Binding Path=LstData, Mode=TwoWay}"
AutoPopulateColumns="False"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AllowColumnMRUFilterList="False"
IsFilterEnabled="False">
Grid.Row="1" x:Name="dgTest"
ShowLoadingPanel="{Binding Path=IsLoading, Mode=TwoWay}"
ItemsSource="{Binding Path=LstData, Mode=TwoWay}"
AutoPopulateColumns="False"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AllowColumnMRUFilterList="False"
IsFilterEnabled="False">
วันจันทร์ที่ 9 กรกฎาคม พ.ศ. 2555
Expand/collapse Group when click on group header
Expand/collapse Group when click on group header
When I have a grid that groups items is there a way to expand/collapse the group when the user clicks on the text in the header? Right now the only way to expand/collapse a group is to click on the expand button in the header.
To accomplish your task handle the MouseLeftButtonUp event in the following manner:
When I have a grid that groups items is there a way to expand/collapse the group when the user clicks on the text in the header? Right now the only way to expand/collapse a group is to click on the expand button in the header.
To accomplish your task handle the MouseLeftButtonUp event in the following manner:
[C#]Open in popup window
private void tableView1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
TableView view = sender as TableView;
TableViewHitInfo hi = view.CalcHitInfo(e.OriginalSource as DependencyObject);
if (hi.HitTest == TableViewHitTest.GroupValue)
ToggleExpandedState(view);
}
private void ToggleExpandedState(TableView view)
{
if (view.Grid.IsGroupRowExpanded(view.FocusedRowHandle))
view.CollapseFocusedRow();
else
view.ExpandFocusedRow();
}
ป้ายกำกับ:
DataGrid,
DEVEXPRESS,
DXGRID,
Silverlight,
Visual Studio
สมัครสมาชิก:
บทความ (Atom)