Hi All,
I am adding one silverlight DataGrid inside the raddropdown button. The idea is we wanted to have a control which will give us a grid in the dropdown in place of a normal list in the dropdown. The problem is when we are adding items source to it and setting the items selection (do remember the dropdown is closed that is isOpen=false) and then when the user clicks on the dropdown button the contol is automatically firing a selection changed event for the DataGrid. To solve this problem we tried assigning the selectionchanged event handler after setting up the itemsource..but still no use. That also does not worked.
What we figured out is as soon as the selection is changed the grid is firing a selction changed event. But as the grid is in closed position the event is not getting properly executed. So as soon as somebody clicks on the open button the event is getting fired. Also inside the selectionchanged event handler we kept the code of closing the dropdown as IsOpen=false. But when it is happening for the first time. That is when after setting the selection of the grid for the first time through code when the user clicks the dropdownbutton then selectionchanged event handler is getting called and as a result the isopen=false is also getting executed. And it is giving a COM error. So we tried executing it through despatcher like this
But as I told you the event handler is getting called for the first time automatically...so we ended up getting a flicker when the user clicks it for the first time. The dropdown opens and closes automically. Then again the user will have to click it and finally the dropdown opens.
So as I told you we need a dropdown control with a grid in it. If you think the control we are using is not correct one..do suggest us. Also If you think there are ways to fix this issue please do let us know. Also below is the whole source code of the control
Hope this helps. Please do let me know if you have any question about any part of the code
Regards
Tiklu
I am adding one silverlight DataGrid inside the raddropdown button. The idea is we wanted to have a control which will give us a grid in the dropdown in place of a normal list in the dropdown. The problem is when we are adding items source to it and setting the items selection (do remember the dropdown is closed that is isOpen=false) and then when the user clicks on the dropdown button the contol is automatically firing a selection changed event for the DataGrid. To solve this problem we tried assigning the selectionchanged event handler after setting up the itemsource..but still no use. That also does not worked.
What we figured out is as soon as the selection is changed the grid is firing a selction changed event. But as the grid is in closed position the event is not getting properly executed. So as soon as somebody clicks on the open button the event is getting fired. Also inside the selectionchanged event handler we kept the code of closing the dropdown as IsOpen=false. But when it is happening for the first time. That is when after setting the selection of the grid for the first time through code when the user clicks the dropdownbutton then selectionchanged event handler is getting called and as a result the isopen=false is also getting executed. And it is giving a COM error. So we tried executing it through despatcher like this
this
.Dispatcher.BeginInvoke(() =>
{
this
.IsOpen =
false
;
});
But as I told you the event handler is getting called for the first time automatically...so we ended up getting a flicker when the user clicks it for the first time. The dropdown opens and closes automically. Then again the user will have to click it and finally the dropdown opens.
So as I told you we need a dropdown control with a grid in it. If you think the control we are using is not correct one..do suggest us. Also If you think there are ways to fix this issue please do let us know. Also below is the whole source code of the control
using
System;
using
System.Windows;
using
System.Windows.Controls;
using
System.Windows.Input;
using
System.Windows.Media;
using
Telerik.Windows.Controls;
using
System.Linq;
using
System.ComponentModel;
using
System.Collections;
using
System.Collections.Generic;
using
System.Windows.Data;
namespace
Phoenix.Controlkit.Module
{
[Description(
"This is a Custom Dropdown Control with a Grid"
)]
public
class
PhoenixCustomDropdown : RadDropDownButton, IPhoenixClearAction
{
#region Private Members
protected
TextBlock lblContent;
protected
DataGrid grid;
protected
int
itemCount;
#endregion
#region CTOR
public
PhoenixCustomDropdown()
:
base
()
{
this
.lblContent =
new
TextBlock();
this
.lblContent.Padding =
new
Thickness(10, 0, 0, 0);
this
.HorizontalContentAlignment = HorizontalAlignment.Left;
this
.Content =
this
.lblContent;
//now the grid
SetupGrid();
//this.Height = 25;
this
.itemCount = 0;
}
/// <summary>
/// This method is actually setting up the grid
/// </summary>
protected
void
SetupGrid()
{
this
.grid =
new
DataGrid();
this
.grid.HeadersVisibility = DataGridHeadersVisibility.Column;
this
.grid.IsReadOnly =
true
;
this
.grid.SelectionMode = DataGridSelectionMode.Single;
this
.grid.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
//this.grid.SelectionChanged += grid_SelectionChanged;
this
.grid.LayoutUpdated +=
new
EventHandler(grid_LayoutUpdated);
this
.grid.MouseLeftButtonDown += (o, e) =>
{
e.Handled =
true
;
};
this
.grid.MouseLeftButtonUp += (o, e) =>
{
this
.Dispatcher.BeginInvoke(() =>
{
this
.IsOpen =
false
;
});
};
SolidColorBrush grdBack =
new
SolidColorBrush(Colors.Transparent);
this
.grid.RowBackground = grdBack;
this
.grid.AlternatingRowBackground = grdBack;
this
.DropDownContent =
this
.grid;
}
private
void
SetupBinding()
{
//for label
var textBinding =
new
Binding(
this
.DisplayMemberPath);
textBinding.Mode = BindingMode.TwoWay;
this
.lblContent.DataContext =
this
.grid.SelectedItem;
this
.lblContent.SetBinding(TextBlock.TextProperty, textBinding);
}
#endregion
#region Event Handlers
protected
override
void
OnKeyDown(KeyEventArgs e)
{
base
.OnKeyDown(e);
switch
(e.Key)
{
case
Key.Down:
if
(
this
.grid.SelectedIndex <
this
.itemCount)
this
.grid.SelectedIndex++;
break
;
case
Key.Up:
if
(
this
.grid.SelectedIndex > 0)
this
.grid.SelectedIndex--;
break
;
}
}
protected
override
void
OnGotFocus(RoutedEventArgs e)
{
base
.OnGotFocus(e);
//this.Background = new SolidColorBrush(Colors.Orange);
this
.grid.Focus();
}
/// <summary>
/// This is the event handler for grids selection change.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void
grid_SelectionChanged(
object
sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
SelectValues();
this
.Dispatcher.BeginInvoke(() =>
{
this
.IsOpen =
false
;
});
}
private
void
SelectValues()
{
this
.lblContent.DataContext =
this
.grid.SelectedItem;
this
.SelectedItem =
this
.grid.SelectedItem;
this
.SelectedValue = GetMemberValue(
this
.SelectedItem);
}
/// <summary>
/// for implementing the onevent pattern
/// </summary>
/// <param name="e"></param>
protected
virtual
void
OnSelectionChanged(System.Windows.Controls.SelectionChangedEventArgs e)
{
if
(
this
.SelectionChanged !=
null
)
{
this
.SelectionChanged(
this
, e);
}
}
void
grid_LayoutUpdated(
object
sender, EventArgs e)
{
double
width = 15;
foreach
(var col
in
this
.grid.Columns)
{
width += col.ActualWidth;
}
this
.grid.Width = (width >
this
.MinWidth) ? width :
this
.MinWidth;
if
(grid.ItemsSource !=
null
)
{
double
height = 12;
foreach
(var item
in
grid.ItemsSource)
{
height += 25;
if
(height > 160)
break
;
}
grid.Height = height;
}
}
#endregion
#region Properties
#region Text
/// <summary>
/// The text property. It returns the text of the
/// current combobox selection
/// </summary>
public
string
Text
{
get
{
return
this
.lblContent.Text;
}
set
{
this
.lblContent.Text = value;
}
}
#endregion
#region GridHeight
public
static
DependencyProperty GridHeightProperty =
DependencyProperty.Register(
"GridHeight"
,
typeof
(
double
),
typeof
(PhoenixCustomDropdown),
new
PropertyMetadata(
(o, e) =>
{
((PhoenixCustomDropdown)o).grid.Height = (
double
)e.NewValue;
}
));
/// <summary>
/// The height of the grid
/// </summary>
public
double
GridHeight
{
get
{
return
(
double
)GetValue(GridHeightProperty);
}
set
{
SetValue(GridHeightProperty, value);
}
}
#endregion
#region ItemsSource
public
static
DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource"
,
typeof
(IEnumerable),
typeof
(PhoenixCustomDropdown),
new
PropertyMetadata(
(o, e) =>
{
if
(e.NewValue ==
null
)
return
;
PhoenixCustomDropdown cdd = (PhoenixCustomDropdown)o;
cdd.grid.SelectionChanged -= cdd.grid_SelectionChanged;
cdd.IsOpen =
true
;
IList lst = (IList)e.NewValue;
cdd.itemCount = lst.Count;
if
(cdd.DefaultValueText !=
null
)
{
if
(cdd.itemCount > 0)
{
var first = lst[0];
var blank = Activator.CreateInstance(first.GetType());
blank.GetType().GetProperty(cdd.DisplayMemberPath).SetValue(blank, cdd.DefaultValueText,
null
);
List<
object
> result =
new
List<
object
>();
result.Add(blank);
result.AddRange(lst);
cdd.grid.ItemsSource = result;
if
(cdd.SelectedValue ==
null
) cdd.SelectedValue = blank.GetType().GetProperty(cdd.ValueMemberPath).GetValue(blank,
null
);
cdd.SetupBinding();
}
else
{
cdd.Text = cdd.DefaultValueText;
cdd.SetupGrid();
//lets try resetting the grid once
cdd.SetupColumns(cdd.Columns);
cdd.grid.ItemsSource = lst;
cdd.grid.SelectedItem =
null
;
}
}
else
{
cdd.grid.ItemsSource = lst;
cdd.grid.SelectedItem =
null
;
}
if
(cdd.DefaultDisplayColumn !=
null
)
{
var deflt = (
from
object
itm
in
cdd.grid.ItemsSource
where (
bool
)itm.GetType().GetProperty(cdd.DefaultDisplayColumn).GetValue(itm,
null
)
select itm
).FirstOrDefault();
if
(deflt !=
null
)
{
if
(cdd.SelectedValue ==
null
) cdd.SelectedValue = deflt.GetType().GetProperty(cdd.ValueMemberPath).GetValue(deflt,
null
);
}
}
cdd.SetSelectionFromValue();
cdd.Dispatcher.BeginInvoke(() =>
{
cdd.IsOpen =
false
;
cdd.grid.SelectionChanged += cdd.grid_SelectionChanged;
});
}
));
/// <summary>
/// The items source to be used for displaying the items in the grid
/// </summary>
public
IEnumerable ItemsSource
{
get
{
return
this
.grid.ItemsSource;
}
set
{
SetValue(ItemsSourceProperty, value);
}
}
#endregion
#region SelectedItem
public
static
DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
"SelectedItem"
,
typeof
(
object
),
typeof
(PhoenixCustomDropdown),
new
PropertyMetadata(
(o, e) =>
{
var cdd = (PhoenixCustomDropdown)o;
List<
object
> addeditems =
new
List<
object
>();
List<
object
> removeditems =
new
List<
object
>();
addeditems.Add(e.NewValue);
removeditems.Add(e.OldValue);
cdd.OnSelectionChanged(
new
System.Windows.Controls.SelectionChangedEventArgs(removeditems, addeditems));
}
));
/// <summary>
/// This property gives the selected item
/// </summary>
[Description(
"This will return a single selected item from the items source"
)]
public
object
SelectedItem
{
get
{
return
((
object
)(GetValue(PhoenixCustomDropdown.SelectedItemProperty)));
}
set
{
SetValue(PhoenixCustomDropdown.SelectedItemProperty, value);
}
}
#endregion
#region DisplayMemberPath
public
static
DependencyProperty DisplayMemberPathProperty =
DependencyProperty.Register(
"DisplayMemberPath"
,
typeof
(
string
),
typeof
(PhoenixCustomDropdown),
null
);
/// <summary>
/// Here you can set column name of the grid to be displayed in the selected textbox
/// </summary>
public
string
DisplayMemberPath
{
get
{
return
((
string
)(
this
.GetValue(PhoenixCustomDropdown.DisplayMemberPathProperty)));
}
set
{
this
.SetValue(PhoenixCustomDropdown.DisplayMemberPathProperty, value);
}
}
#endregion
#region DefaultText
/// <summary>
/// This is the text which is going to come when there
/// is no other values added to it.
/// for example it will display none if you select nothing.
/// </summary>
[Description(
"Add the default value text which will be displayed by default in case there is no data selected"
)]
public
string
DefaultValueText
{
get
;
set
;
}
#endregion
#region DefaultDisplayColumn
[Description(
"Here you can add the default display column name which will be used for selecting the default value"
)]
public
string
DefaultDisplayColumn
{
set
;
get
;
}
#endregion
#endregion
#region copied code
/// <summary>
/// Returns the value from a particular record
/// </summary>
/// <param name="item">The single record to evaluate</param>
/// <returns>The value memeber value</returns>
private
object
GetMemberValue(
object
item)
{
return
item.GetType().GetProperty(ValueMemberPath).GetValue(item,
null
);
}
private
object
GetMemberDisplay(
object
item)
{
try
{
return
item.GetType().GetProperty(DisplayMemberPath).GetValue(item,
null
);
}
catch
(Exception)
{
return
null
;
}
}
public
static
DependencyProperty ValueMemberPathProperty =
DependencyProperty.Register(
"ValueMemberPath"
,
typeof
(
string
),
typeof
(PhoenixCustomDropdown),
null
);
/// <summary>
/// The name of the colum whos value will be returned while a single item is selected
/// </summary>
public
string
ValueMemberPath
{
get
{
return
((
string
)(
base
.GetValue(PhoenixCustomDropdown.ValueMemberPathProperty)));
}
set
{
base
.SetValue(PhoenixCustomDropdown.ValueMemberPathProperty, value);
}
}
public
static
DependencyProperty SelectedValueProperty =
DependencyProperty.Register(
"SelectedValue"
,
typeof
(
object
),
typeof
(PhoenixCustomDropdown),
new
PropertyMetadata((o, e) =>
{
//((PhoenixCustomDropdown)o).SetSelectionFromValue();
}));
/// <summary>
/// The selected value,based on the selection
/// </summary>
public
object
SelectedValue
{
get
{
return
GetValue(PhoenixCustomDropdown.SelectedValueProperty);
}
set
{
SetValue(PhoenixCustomDropdown.SelectedValueProperty, value);
}
}
private
void
SetSelectionFromValue()
{
var value = SelectedValue;
if
(ItemsSource !=
null
&& value !=
null
)
{
var sel = (from
object
item
in
this
.grid.ItemsSource
where GetMemberValue(item).Equals(value)
select item).FirstOrDefault();
if
(sel ==
null
)
{
//try to get the blank value
sel = (from
object
itm
in
this
.grid.ItemsSource where GetMemberDisplay(itm).Equals(
this
.DefaultValueText) select itm).FirstOrDefault();
}
if
(sel !=
null
)
{
this
.grid.SelectionChanged -=
this
.grid_SelectionChanged;
this
.grid.SelectedItem = sel;
SelectValues();
this
.grid.SelectionChanged +=
this
.grid_SelectionChanged;
}
}
}
#endregion
#region Public Methods
[Description(
"Please do handle this event in case you want to do something specific while the selection of the combobox is changed"
)]
public
event
System.Windows.Controls.SelectionChangedEventHandler SelectionChanged;
protected
string
_cols;
/// <summary>
/// This is where we are going to pass the columns
/// The format should be col1,col2,col3 etc.
/// </summary>
public
string
Columns
{
set
{
this
._cols = value;
SetupColumns(value);
}
get
{
return
this
._cols;
}
}
/// <summary>
/// Setup the grid
/// </summary>
/// <param name="value"></param>
private
void
SetupColumns(
string
value)
{
this
.grid.AutoGenerateColumns =
false
;
this
.grid.Columns.Clear();
foreach
(
string
col
in
value.Split(
','
))
{
string
[] vals = col.Split(
'|'
);
DataGridTextColumn dtcol =
new
DataGridTextColumn();
dtcol.Binding =
new
System.Windows.Data.Binding(vals[0]);
dtcol.Width = DataGridLength.SizeToCells;
dtcol.Header = (vals.Length > 1 ? vals[1] : vals[0]);
this
.grid.Columns.Add(dtcol);
}
if
(grid.Columns.Count == 1)
{
this
.grid.HeadersVisibility = DataGridHeadersVisibility.None;
this
.grid.GridLinesVisibility = DataGridGridLinesVisibility.None;
}
}
#endregion
#region IPhoenixClearAction Members
public
void
Reset()
{
var blank = (from
object
itm
in
this
.ItemsSource where GetMemberDisplay(itm).Equals(
this
.DefaultValueText) select itm).FirstOrDefault();
this
.grid.SelectedItem = blank;
}
#endregion
}
}
Hope this helps. Please do let me know if you have any question about any part of the code
Regards
Tiklu