Telerik Radgrid WPF - wpf

My WPF page has a RadGrid control provided by Telerik. The Grid is a nested grid which essentially means that clicking on the (+) sign on the leftmost column of the row expands the row into a Subgrid. This is being done by specifying a hierarchical grid in my XAML. Everything works just fine when you click on the row and expand the subgrid but the selectedItem of the initially selected row does not seem to change. An example would be selecting row 1 of the grid initially and then expanding row 4 to display the subgrid. The subgrid is displayed but the selectedItem is still row 1. The desired behavior is for row 4 to be the selectedItem once it is expanded to display the subgrid. Can anyone point out what exactly is going wrong over here.
Thanks

Your are right - here is the updated version:
private void RadGridView_Loaded(object sender, RoutedEventArgs e)
{
var childGrid = (RadGridView)sender;
var parentRow = childGrid.ParentRow;
if (parentRow != null)
{
RadGridView1.SelectedItem = childGrid.DataContext;
parentRow.IsExpandedChanged += new RoutedEventHandler(parentRow_IsExpandedChanged);
}
}
void parentRow_IsExpandedChanged(object sender, RoutedEventArgs e)
{
RadGridView1.SelectedItem = ((GridViewRow)sender).DataContext;
}

Here is an example:
XAML
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
Title="Window1">
<Grid>
<telerik:RadGridView x:Name="RadGridView1" ItemsSource="{Binding}">
<telerik:RadGridView.ChildTableDefinitions>
<telerik:GridViewTableDefinition />
</telerik:RadGridView.ChildTableDefinitions>
<telerik:RadGridView.HierarchyChildTemplate>
<DataTemplate>
<telerik:RadGridView ItemsSource="{Binding Items}" Loaded="RadGridView_Loaded" />
</DataTemplate>
</telerik:RadGridView.HierarchyChildTemplate>
</telerik:RadGridView>
</Grid>
C#
using System.Windows;
using System.Linq;
namespace WpfApplication1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = from i in Enumerable.Range(0, 10)
select new
{
ID = i,
Items = from j in Enumerable.Range(0, 10)
select new
{
ID = j,
}
};
}
private void RadGridView_Loaded(object sender, RoutedEventArgs e)
{
RadGridView1.SelectedItem = ((FrameworkElement)sender).DataContext;
}
}
}

Related

How do you make the WPF Datagrid select a cell when you first tab into it

When I tab into the WPF Datagrid it focuses the first cell (with a rectangle) but does not select it (in blue). If I press tab again it focuses and selects it.
I think the DataGridCell actually has IsSelected=true, but it is not being painted in blue. I have tried hacking around with the datagrid and visual-states but I can't make it repaint the grid correctly when you first tab in.
Has anyone seen this before and do you have a solution?
code to reproduce:
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TextBox Width="100"/>
<DataGrid SelectionMode="Single" SelectionUnit="Cell"
ItemsSource="{Binding MyItems}" AutoGenerateColumns="True"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
using System.Collections.Generic;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyItems.Add(new Thingy() { Name = "Frank", Age = 34 });
MyItems.Add(new Thingy() { Name = "Jim", Age = 43 });
MyItems.Add(new Thingy() { Name = "Bob", Age = 56 });
MyItems.Add(new Thingy() { Name = "Harry", Age = 23 });
DataContext = this;
}
private List<Thingy> _myItems = new List<Thingy>();
public List<Thingy> MyItems
{
get { return _myItems; }
}
}
public class Thingy
{
public string Name { get; set; }
public int Age { get; set; }
}
}
click on the TextBox, then hit tab --- cell 1 is not selected
hit tab again --- cell 2 is selected
Any help is much appreciated, thanks.
Update:
When SelectionUnit=FullRow, I have had some success along the lines shown below, if SelectedIndex is set to 0 upon creation the first row is now selected in blue. It still needs some work to cope with shift-tab etc. There is still a problem though because when I change the SelectionMode to extended and press shift-downarrow the second row gets selected but the first row gets unselected (they should both be selected). If I do it again rows 2+3 are selected which is correct and it continues to work ok after that.
protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
{
base.OnIsKeyboardFocusWithinChanged(e);
int oldIdx = this.SelectedIndex;
this.SelectedIndex = -1;
this.SelectedIndex = oldIdx;
}
Further Update:
Fixed that issue by setting the private _selectionAnchor field. (Thanks ILSpy)
protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
{
base.OnIsKeyboardFocusWithinChanged(e);
this.SelectedIndex = -1;
this.SelectedIndex = 0;
SelectionAnchor = SelectedCells[0];
}
protected DataGridCellInfo? SelectionAnchor
{
get
{
return typeof(DataGrid).GetField("_selectionAnchor", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this) as DataGridCellInfo?;
}
set
{
typeof(DataGrid).GetField("_selectionAnchor", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(this, value);
}
}
I know my answer is too late but it would help other navigating to this site.
After lot of research, I got the answer on how to select the element while tabbing.
It was really easy and was a single line of code in XAML that did the trick;
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="IsTabStop" Value="False"/>
</Style>
By setting IsTabStop to false you are telling the datagridcell's visual tree to go inside its template and find any element that is focus able. If it finds some element then it focuses that element.
You can do like this. Register for a got focus event and then set the original source as selected item.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TextBox Width="100"/>
<DataGrid SelectionMode="Single" SelectionUnit="Cell"
ItemsSource="{Binding MyItems}" AutoGenerateColumns="True"
GotFocus="WPF_DataGrid_GotFocus" />
</StackPanel>
</Window>
Then in the code behind file :
private void WPF_DataGrid_GotFocus(object sender, RoutedEventArgs e)
{
(e.OriginalSource as DataGridCell).IsSelected = true;
}
I hope it helps!
If CodeBehind is an option the following code sets the selectedItem:
private void CrashGrid_OnGotKeyboardFocus(object sender, RoutedEventArgs e)
{
(DataGrid)e.Source.SelectedItem = (DataGrid)e.Source.CurrentCell.Item;
}

Behaviour of ItemsControl

I have a screen with several UserControls, but only one of them remains active. The other UserControls aren't shown, but the user can switch the active flag of any of those who are not active. One of the UserControl contains an ItemsControl.
I need to know all the controls in the view, including those generated by an ItemsControl, after loading the first UserControl that is active in the screen, when view is finally initialized.
For ItemsControl, wpf didn't instance any item until it was painted on the screen that contains the UserControl (so I've tried, until the Load event is launched), so that I can't found the controls contained by the view because it didn't exist.
Is there any way to change this behavior?
I try to change the value of property VirtualizingStackPanel.IsVirtualizing to false, to avoid the previous behaviour, with no success. To illustrate this, I write this view example:
<Window x:Class="ContenidoEnTabs.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel x:Name="spContainer" Orientation="Vertical" VirtualizingStackPanel.IsVirtualizing="False">
<Button Content="Push" Click="Button_Click" />
</StackPanel>
</Window>
This view creates a second control not visible until the user press the button:
public partial class MainWindow : Window
{
private NotPaintedOnInitUserControl controlExtra;
public MainWindow()
{
InitializeComponent();
controlExtra = new NotPaintedOnInitUserControl();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
spContainer.Children.Add(controlExtra);
}
}
The control not visible initially is as follow:
<UserControl x:Class="ContenidoEnTabs.NotPaintedOnInitUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<ItemsControl ItemsSource="{Binding MyCollection}" x:Name="itemsControlTarget"
VirtualizingStackPanel.IsVirtualizing="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox x:Name="aTextBox" Width="80" Initialized="ATextBox_Initialized" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
and in CodeBehind I detect when the Items were created
public partial class NotPaintedOnInitUserControl : UserControl
{
public NotPaintedOnInitUserControl()
{
InitializeComponent();
DataContext = new SimpleListDataContext();
}
private void ATextBox_Initialized(object sender, EventArgs e)
{
}
}
And the DataContext used:
public class SimpleListDataContext
{
private List<string> _myCollection;
public List<string> MyCollection
{
get { return _myCollection ?? (_myCollection = new List<string> { "one", "two" }); }
set { _myCollection = value; }
}
}
Any ideas?
Thanks in advance.
If you want WPF to generate the tree for a control that isn't part of the view, you can "hydrate" and layout the control by forcing the layout to run. Something like this should work:
public partial class MainWindow : Window
{
private NotPaintedOnInitUserControl controlExtra;
public MainWindow()
{
InitializeComponent();
controlExtra = new NotPaintedOnInitUserControl();
// Force the control to render, even though it's not on the screen yet.
var size = new Size(this.Width, this.Height);
var rect = new Rect(new Point(0,0), size);
controlExtra.Measure(size);
controlExtra.Arrange(rect);
controlExtra.InvalidateVisual();
controlExtra.UpdateLayout();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
spContainer.Children.Add(controlExtra);
}
}
Not sure if this is what you're asking. If not, please clarify paragraph 2.
Have a look at LogicalTreeHelper.GetChildren(myUiElement)
This looks at the logical tree rather than the visual tree so it examines the structure without needing to have loaded the control to get the visual structure
In the below control to find is the name of the contorl i.e. myDatagrid
You could also adapt this to just get all the children of a particular control i.e.
FindChildInVisualTree(this, "mydatagrid"); // assumming this a UIElement (i.e. your in the code behind)
find the control using the below then using LogicalTreeHelper get all it's children.
public static UIElement FindChildInVisualTree(UIElement view, string controlToFind)
{
UIElement control = null;
try
{
if (view != null)
{
if ((view as FrameworkElement).Name.ToUpper() == controlToFind.ToUpper())
{
control = view;
}
else
{
DependencyObject depObj = view as DependencyObject;
if (depObj != null)
{
foreach (var item in LogicalTreeHelper.GetChildren(depObj))
{
control = FindChildInVisualTree(item as UIElement, controlToFind);
if (control != null)
{
break;
}
}
}
}
}
}
catch (Exception ex)
{
throw new ApplicationException("Error finding child control: " + controlToFind, ex);
}
return control;
}

WPF: selected ListView items after showing a dialog window

I have an action Edit in my WPF application, which is bound to items in a ListView control, i.e. it is executed when an item is double clicked or the Edit button in the toolbar is clicked. This action in turn displays a modal window with the editing stuff.
Now when I select multiple items in the list, click Edit, the items stay selected in the background, also, when I close the dialog, they are still selected in the sence that their background is blue. However, they seem to be not selected in the sence that the Edit button is disabled in the toolbar (the Edit action's CanExecute method simply checks FileList.SelectedIndex != -1. What's more, the "selected" items won't get deselected when I click some other list item - they only get deselected when I explicitly click on them one by one - it's as if the blue background is stuck on them.
My code does not use any fancy ListView styles or what not, so what could be causing this ?
I can post my code upon request, but it is pretty much standard.
EDIT:
After cutting down my code I finally found what's causing this issue. After showing the dialog, I edit the items in the data bound collection, so that the ListView would get updated (i.e. replace the bound objects to new objects). The question is, why is this causing a problem and how should I resolve it ?
Something in your code must be causing this issues. Below is a sample which behaves as expected.
XAML:
<Window x:Class="TestDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<RoutedUICommand x:Key="EditItemsCommand" Text="Edit Items" />
</Window.Resources>
<Window.CommandBindings>
<CommandBinding
Command="{StaticResource EditItemsCommand}"
CanExecute="EditItems_CanExecute"
Executed="EditItems_Executed" />
</Window.CommandBindings>
<StackPanel>
<Button Name="_editButton" Content="Edit" Command="{StaticResource EditItemsCommand}" />
<Button Content="Unselect all" Click="OnUnselectAll" />
<ListView
Name="_listView"
ItemsSource="{Binding Path=Items}"
SelectionMode="Extended"
MouseDoubleClick="OnListViewMouseDoubleClick">
</ListView>
</StackPanel>
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
namespace TestDemo
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public IEnumerable<string> Items
{
get
{
for (int i = 0; i < 10; i++) { yield return i.ToString(); }
}
}
private void EditItems_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = _listView != null && _listView.SelectedItems.Count > 0;
}
private void EditItems_Executed(object sender, ExecutedRoutedEventArgs e)
{
EditWindow editWindow = new EditWindow();
editWindow.EditItems = _listView.SelectedItems.Cast<string>();
editWindow.ShowDialog();
}
private void OnListViewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
_editButton.Command.Execute(null);
}
private void OnUnselectAll(object sender, RoutedEventArgs e)
{
_listView.SelectedItem = null;
}
}
}
XAML:
<Window x:Class="TestDemo.EditWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="EditWindow">
<ListBox ItemsSource="{Binding Path=EditItems}" />
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Windows;
namespace TestDemo
{
public partial class EditWindow : Window
{
public EditWindow()
{
InitializeComponent();
DataContext = this;
}
public IEnumerable<string> EditItems { get; set; }
}
}
What do you have ListView.SelectionMode set to? It sounds like it is set to Multiple (clicking an item extends the selection), while you might want to set it to Extended (selection is extended when clicking an item and pressing Control or Shift) instead.
I'm not sure what to say about the Edit command problem, though. Maybe there is an odd behavior with SelectedIndex and multiple selection - possibly check the count of the objects in the ListView.SelectedItems collection instead?

Silverlight Datagrid Row Click

I have a datagrid with a column containing a checkbox. I want to change the value of the bound Selected property when the row is clicked:
alt text http://lh4.ggpht.com/_L9TmtwXFtew/Sw6YtzRWGEI/AAAAAAAAGlQ/pntIr2GU6Mo/image_thumb%5B3%5D.png
NOTE: I don't want to use the SelectedItemChanged event because this doesn't work properly when there is only one row in the grid.
As is often the way i have found my own solution for this:
Add a MouseLeftButtonUp event to the datagrid:
<data:DataGrid x:Name="dgTaskLinks"
ItemsSource="{Binding TaskLinks}"
SelectedItem="{Binding SelectedTaskLink, Mode=TwoWay}"
MouseLeftButtonUp="dgTaskLinks_MouseLeftButtonUp"
>...
And walk the visual tree to get the data grid row:
private void dgTaskLinks_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
///get the clicked row
DataGridRow row = MyDependencyObjectHelper.FindParentOfType<DataGridRow>(e.OriginalSource as DependencyObject);
///get the data object of the row
if (row != null && row.DataContext is TaskLink)
{
///toggle the IsSelected value
(row.DataContext as TaskLink).IsSelected = !(row.DataContext as TaskLink).IsSelected;
}
}
Once found, it is a simple approach to toggle the bound IsSelected property :-)
Hope this helps someone else.
Here is an even simpler solution
XAML
<data:DataGrid
x:Name="dgMyDataGrid"
ItemsSource="{Binding MyList}"
SelectedItem="{Binding MyList, Mode=TwoWay}"
MouseLeftButtonUp="dgMyDataGrid_MouseLeftButtonUp">...
CS
private void dgMyDataGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
DataGrid dg = (sender as DataGrid);
var allObjects = dg.DataContext as List<MyCustomObject>;
foreach(var o in allObjects)
{
o.Selected = false;
}
MyCustomObject SelectedObject = (MyCustomObject)dg.SelectedItem;
SelectedObject.Selected = true;
}
Note: this as well as the other example assumes your class that you are binding to the control implements INotifyPropertyChanged

Find Silverlight TreeViewItem control by Header

I am trying to create a TreeView from the Silverlight TreeView control. I have my data being pulled from a WCF service that pulls from EF. All of the data is coming in fine. I have the page set up where I can input a UserName, click a button, and the data will populate the first generation in the TreeView. So, I'm dynamically building TreeViewItems to put into my TreeView with a Selected RoutedEventHandlers attached to each one. When I click on one of the TreeViewItem nodes, it kicks off the tvi_Selected function in which I want to populate TreeViewItems under the TreeViewItem that I just selected.
I run into problem when I am in my delegate function prox_GetChildMembersCompleted. I can't figure out a way to do a FindControl type lookup on the TreeViewItem that I want to add the child TreeViewItem elements to. So, I thought that I would just create a protected field where I would store the Header information to because it contain only the UserName. I just need to be able to access a specific TreeViewItem by Header or some other method that is alluding me.
You can see that in my Selected eventhandler, that I am getting the Header info by casting the sender object to a TreeViewItem. In the the delegate function prox_GetChildMembersCompleted that is called inside of tvi_Selected, the sender object is WCFDataClient so I can't grab the same data from that sender. Any insight into this would be much appreciated even if you suggest a method that is completely different.
<UserControl xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
x:Class="FloLOS2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot" Background="#5C7590">
<StackPanel>
<TextBox x:Name="txtUserName" Width="120" Margin="5"></TextBox>
<TextBlock x:Name="txtFillBlock" Width="300" Margin="5" Foreground="White" Text="Change me"></TextBlock>
<Button x:Name="btnSubmit" Margin="5" Content="Get Frontline" Width="120" Click="btnSubmit_Click" />
<data:DataGrid x:Name="MembersGrid" Margin="5"></data:DataGrid>
<controls:TreeView x:Name="MembersTree" Margin="5"></controls:TreeView>
</StackPanel>
</Grid>
</UserControl>
namespace FloLOS2
{
public partial class MainPage : UserControl
{
string sParentID;
public MainPage()
{
InitializeComponent();
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
GetMyDataRef.GetMyDataClient prox = new FloLOS2.GetMyDataRef.GetMyDataClient();
prox.GetMembersCompleted += new EventHandler<FloLOS2.GetMyDataRef.GetMembersCompletedEventArgs>(prox_GetMembersCompleted);
prox.GetMembersAsync(txtUserName.Text);
}
void prox_GetMembersCompleted(object sender, FloLOS2.GetMyDataRef.GetMembersCompletedEventArgs e)
{
GetMyDataRef.Member[] members = e.Result.ToArray();
foreach (var x in members)
{
TreeViewItem tvi = new TreeViewItem() { Header = x.UserName };
tvi.Selected += new RoutedEventHandler(tvi_Selected);
MembersTree.Items.Add(tvi);
}
//MembersTree.Items.Add(tvi);
}
void prox_GetChildMembersCompleted(object sender, FloLOS2.GetMyDataRef.GetMembersCompletedEventArgs e)
{
GetMyDataRef.Member[] members = e.Result.ToArray();
TreeViewItem tviParent = new TreeViewItem();
// *** Find TreeViewItem control based on Header ***
foreach (var x in members)
{
TreeViewItem tviChild = new TreeViewItem() { Header = x.UserName };
tviChild.Selected += new RoutedEventHandler(tvi_Selected);
tviParent.Items.Add(tviChild);
}
}
void tvi_Selected(object sender, RoutedEventArgs e)
{
try
{
TreeViewItem item = (TreeViewItem)sender;
txtFillBlock.Text = item.Header.ToString();
sParentID = item.Header.ToString();
GetMyDataRef.GetMyDataClient prox = new FloLOS2.GetMyDataRef.GetMyDataClient();
prox.GetMembersCompleted += new EventHandler<FloLOS2.GetMyDataRef.GetMembersCompletedEventArgs>(prox_GetChildMembersCompleted);
prox.GetMembersAsync(item.Header.ToString());
}
catch (Exception ex)
{
txtFillBlock.Text = ex.InnerException.ToString();
}
}
}
}
I figured out a way to do it. I went and assigned a Name to the dynamically generated TreeViewItems as the UserName. I also stored the sender UserName in a protected string, then called this line of code to get the parent TreeViewItem:
TreeViewItem tviParent = (TreeViewItem)LayoutRoot.FindName(sParentID);
Thanks for what would have been great answers! :)

Resources