How to find the ParentComboBox of a ComboBoxItem? - c#

How can I get the ParentComboBox of an ComboBoxItem?
I would like to close an open ComboBox if the Insert-Key is pressed:
var focusedElement = Keyboard.FocusedElement;
if (focusedElement is ComboBox)
{
var comboBox = focusedElement as ComboBox;
comboBox.IsDropDownOpen = !comboBox.IsDropDownOpen;
}
else if (focusedElement is ComboBoxItem)
{
var comboBoxItem = focusedElement as ComboBoxItem;
var parent = comboBoxItem.Parent; //this is null
var parent = comboBoxItem.ParentComboBox; //ParentComboBox is private
parent.IsDropDownOpen = !parent.IsDropDownOpen;
}
It looks like there's no straight forward solution for this problem..

Basically, you want to retrieve an ancestor of a specific type. To do that, I often use the following method :
public static class DependencyObjectExtensions
{
public static T FindAncestor<T>(this DependencyObject obj) where T : DependencyObject
{
return obj.FindAncestor(typeof(T)) as T;
}
public static DependencyObject FindAncestor(this DependencyObject obj, Type ancestorType)
{
var tmp = VisualTreeHelper.GetParent(obj);
while (tmp != null && !ancestorType.IsAssignableFrom(tmp.GetType()))
{
tmp = VisualTreeHelper.GetParent(tmp);
}
return tmp;
}
}
You can use it as follows :
var parent = comboBoxItem.FindAncestor<ComboBox>();

As H.B. wrote you also can use
var parent = ItemsControl.ItemsControlFromItemContainer(comboBoxItem) as ComboBox;

Related

How to get Datepicker from Calendar in WPF

Is there any way to get datepicker from the calendar.
I'm trying this extension method but it returns null.
private void ChangeCalendarToToday(System.Windows.Controls.Calendar obj)
{
var parent=obj.GetParentOfType<DatePicker>();
obj.SelectedDate = DateTime.Now;
obj.DisplayDate = DateTime.Now;
obj.DisplayMode = CalendarMode.Month;
}
public static T GetParentOfType<T>(this DependencyObject child) where T : DependencyObject
{
//get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
return parent;
else
return GetParentOfType<T>(parentObject);
}
Thanks!

MVVM Behavior in another assembly

I created a assembly for my MVVM Project in WPF. In my assembly i have a Behavior for activate when I want to sort a column in a datagrid.
Behavior SortColumn
public class SortColumn : Behavior<DataGrid>
{
public string Property = "";
public bool MeaningSort = true;
public static DependencyProperty AtSortingColumnCommandProperty = DependencyProperty.RegisterAttached(
"AtSortingColumnCommand", typeof(ICommand),
typeof(SortColumn));
public static ICommand GetAtSortingColumnCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(AtSortingColumnCommandProperty);
}
public static void SetAtSortingColumnCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(AtSortingColumnCommandProperty, value);
}
protected override void OnAttached()
{
AssociatedObject.Sorting += AssociatedObject_Sorting;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.Sorting -= AssociatedObject_Sorting;
base.OnDetaching();
}
//Selon MeaningSort, on renvoie une chaine OrderBy en ASC ou DESC
//Ex: MonChamp ASC
private void AssociatedObject_Sorting(object sender, DataGridSortingEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
string FiledName = e.Column.SortMemberPath;
if (Property == null || (Property != FiledName && MeaningSort != false))
{
e.Column.SortDirection = ListSortDirection.Ascending;
MeaningSort = false;
var atEnd = GetAtSortingColumnCommand(element);
if (atEnd != null)
{
atEnd.Execute(FiledName + " ASC");
}
}
else
{
e.Column.SortDirection = ListSortDirection.Descending;
MeaningSort = true;
var atEnd = GetAtSortingColumnCommand(element);
if (atEnd != null)
{
atEnd.Execute(FiledName + " DESC");
}
}
}
}
And in my XAML
<D:DataGridTemplate x:Name="Datagrid"
TablePaged:ScrollViewerMonitor.AtEndCommand="{Binding LoadCommand}"
TablePaged:SortColumn.AtSortingColumnCommand="{Binding SortingColumnCommand}"
Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" ItemsSource="{Binding DataProduits,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
So it works fine ONLY when my file is in the same assembly. Maybe i'm not on the same Instance ?
Attaching a behavior is quite intuitive, and should be done in the following manner:
1) Include the following xmlns in the document header
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
2) Define the xmlns in which the behavior resides (Also in the document header)
xmlns:b="clr-namespace:MyAssembly.MyBehaviors;assembly=MyAssembly"
3) Attach the behavior to the element in the following manner:
<Element>
<i:Interaction.Behaviors>
<b:MyBehavior MyDependencyProperty={Binding Foo}>
</i:Interaction.Behaviors>
</Element>
As long that you follow these steps, the behavior will become attached, but mind this:
In order for the behavior to attach, the element to which the behavior is attached must be a subclass of the type specified by the behavior's generic parameter (if you are using the generic Behavior base class).
Alternatively, it must be a subclass of DependencyObject if you are using the non generic Behavior base class.
EDIT 1:
The behavior you implemented is for a DataGrid, yet your XAML attached it to the DataGridTemplate class, all this while the Attached Property is registered to your behavior instead of to some TargetType.. it really seems all over the place...
EDIT 2:
It seems that you took the attached behavior approach, which kind of makes subclassing behavior pointless..
If you already took the time to subclass Behavior, you might as well make those properties regular Dependency Properties.
thank you very much ! Thanks to your advice, I was able to correct the problem. The code following code works perfectly from another Assembly
public class SortColumn
{
public static string Property = "";
public static bool MeaningSort = true;
public static DependencyProperty AtSortingColumnCommandProperty = DependencyProperty.RegisterAttached("AtSortingColumnCommand",
typeof(ICommand), typeof(SortColumn), new PropertyMetadata(OnAtSortingColumnCommandChanged));
public static ICommand GetAtSortingColumnCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(AtSortingColumnCommandProperty);
}
public static void SetAtSortingColumnCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(AtSortingColumnCommandProperty, value);
}
public static void OnAtSortingColumnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var myDataGrid = d as DataGrid;
if (myDataGrid != null)
{
myDataGrid.Sorting -= HandleColumnSorting;
myDataGrid.Sorting += HandleColumnSorting;
}
}
static void HandleColumnSorting(object sender, DataGridSortingEventArgs e)
{
var myDataGridSorting = sender as DataGrid;
string FiledName = e.Column.SortMemberPath;
if (Property == null || (Property != FiledName && MeaningSort != false))
{
e.Column.SortDirection = ListSortDirection.Ascending;
MeaningSort = false;
var atEnd = GetAtSortingColumnCommand(myDataGridSorting);
if (atEnd != null)
{
atEnd.Execute(FiledName + " ASC");
}
}
else
{
e.Column.SortDirection = ListSortDirection.Descending;
MeaningSort = true;
var atEnd = GetAtSortingColumnCommand(myDataGridSorting);
if (atEnd != null)
{
atEnd.Execute(FiledName + " DESC");
}
}
}
}

Adding a control to the main container of unknown object

void Visualize(object CoreObj, object ParentControl)
{
if(CoreObj is typeA)
{
object control1 = new MyControl1(CoreObj);
ParentControl.FirstChild.Children.Add(control1);
foreach (object obj in CoreObj.Children)
{
Visualize(obj, control1);
}
}
else if (CoreObj is typeB)
{
object control2 = new MyControl2(CoreObj);
ParentControl.FirstChild.Children.Add(control2);
foreach (object obj in CoreObj.Children)
{
Visualize(obj, control2);
}
}
}
Where FirstChild is always container, no matter StackPanel, Grid or Canvas, or whatever.
How I get the first child, and the harder part, how to do Children.Add() on object?
I can require something else in case "Children" is inherited from somewhere in all wpf containers, but I can't find out which ancestor/interface contains "Children". Or I can use Reflection probably..
How to do this?
Here's what I came with, finally
interface IContain
{
Panel GetMain(); //return main container
}
// ...
void Visualize(object CoreObj, Panel ParentControl)
{
UIElement control = new UIElement();
if (CoreObj is File) { control = new NameSpacer(); } //new NameSpacer(obj);
else if (CoreObj is Namespace) { control = new NameSpacer(); }
else if(CoreObj is Using) { control = new NameSpacer(); }
if (control.GetType() == typeof(UIElement)) return;
ParentControl.Children.Add(control);
FieldInfo finf = CoreObj.GetType().GetField("Children"); if (finf == null) return;
var val = finf.GetValue(CoreObj); if (val.GetType() != typeof(IEnumerable<object>)) return;
if (control is IContain == false) return;
Panel container = ((IContain)control).GetMain();
foreach (object o in val as IEnumerable<object>)
{
Visualize(o, container);
}
}
You can use VisualTreeHelper class to get the first child.
Children property is defined in abstract class Panel.
var firstChild = parentControl.Descendants().OfType<Panel>().First();
firstChild.Children.Add(control1);
The descendants method leverages VisualTreeHelper in order to get all descendants and you have to define it as extension method:
public static IEnumerable<DependencyObject> Descendants(this DependencyObject element)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < childrenCount; i++)
{
var visualChild = VisualTreeHelper.GetChild(element, i);
yield return visualChild;
foreach (var visualChildren in Descendants(visualChild))
{
yield return visualChildren;
}
}
}
StackPanel, Grid and Canvas, all derive from the Panel class. This is also the class that contains the Children property.
if you know your argument is a Panel, you also have access to Children.First() and Children.Add(..)

C# : How to cast an object to ItemSource data type?

I am trying to develop a custom User control. In my User Control I use two controls a List Box and a Text Box. Text Box is use for filtering items in List Box. To doing this I am facing a problem in my filter method. In my filter method I need to cast object to ItemSource type. But I don`t understand how can I cast it. Here is my Code which I try:
public static readonly DependencyProperty ItemSourceProperty = DependencyProperty.Register("ItemSourrce", typeof(IEnumerable), typeof(SSSearchListBox), new PropertyMetadata(new PropertyChangedCallback(OnItemsSourcePropertyChanged)));
private static void OnItemsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as SSSearchListBox;
if (control != null)
control.OnItemsSourceChanged((IEnumerable)e.OldValue, (IEnumerable)e.NewValue);
}
private void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
// Remove handler for oldValue.CollectionChanged
var oldValueINotifyCollectionChanged = newValue as INotifyCollectionChanged;
if (null != oldValueINotifyCollectionChanged)
{
oldValueINotifyCollectionChanged.CollectionChanged -= new NotifyCollectionChangedEventHandler(newValueINotifyCollectionChanged_CollectionChanged);
}
// Add handler for newValue.CollectionChanged (if possible)
var newValueINotifyCollectionChanged = newValue as INotifyCollectionChanged;
if (null != newValueINotifyCollectionChanged)
{
newValueINotifyCollectionChanged.CollectionChanged += new NotifyCollectionChangedEventHandler(newValueINotifyCollectionChanged_CollectionChanged);
}
}
void newValueINotifyCollectionChanged_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//Do your stuff here.
}
public IEnumerable ItemSourrce
{
get { return (IEnumerable)this.GetValue(ItemSourceProperty); }
set { this.SetValue(ItemSourceProperty, value); }
}
public void TextFiltering(ICollectionView filteredview, DevExpress.Xpf.Editors.TextEdit textBox)
{
string filterText = "";
filteredview.Filter = delegate(object obj)
{
if (String.IsNullOrEmpty(filterText))
{
return true;
}
string str = obj as string; // Problem is here.
// I need to cast obj to my ItemSourrce Data Type.
if (String.IsNullOrEmpty(obj.ToString()))
{
return true;
}
int index = str.IndexOf(filterText, 0, StringComparison.InvariantCultureIgnoreCase);
return index > -1;
};
textBox.EditValueChanging += delegate
{
filterText = textBox.Text;
filteredview.Refresh();
};
}
private void textEdit1_GotFocus(object sender, RoutedEventArgs e)
{
ICollectionView view = CollectionViewSource.GetDefaultView(ItemSourrce);
TextFiltering(view, textEdit1);
}
calling this Use Control :
List<testClass> myList = new List<testClass>();
public void testMethod()
{
for (int i = 0; i < 20; i++)
{
myList.Add(new testClass { testData=i.ToString()});
}
myTestControl.ItemSourrce = myList;
}
public class testClass
{
public string testData { get; set; }
}
thank`s
well everything what you want to assing to ItemSource (or ItemsSource for naming consistence) has to implement the IEnumerable interface. Thist means basicaly erverything you can iterate through a foreach (to put it simply). So every List, array, collections, Dictionaries and so on.
Normaly you can't iterate throu a normal out of the box string!
So if your Object is realy a String (check it with a breakpoint on debugging) you have to split it somehow.
ps: by the way this code
if (String.IsNullOrEmpty(obj.ToString()))
{
return true;
}
never (except some rare unmeaningfull example where you explicitly return an empty string for ToStirng()) returns true because obj.ToString() is never null or Empty. In the standard implementation of the type Object (which every .net Type is based on) it returns the Namespace and the name of the type.
If I understood well your objects are of type testClass and not of string.
So you code should be where you try to do the casting.
string str = string.Empty;
testClass myObject = obj as testClass;
if (myObject == null)
return true;
else
str = myObject.testData;
if (string.IsNullOrEmpty(str))
return true;
return str.IndexOf(filterText, 0, StringComparison.InvariantCultureIgnoreCase) > -1;

How to set focus to a control with Caliburn.Micro MVVM

I have a form and I want to set the focus to a text box when certain user actions happen. I know the MVVM way of doing things is to bind to VM properties, however the TextBox does not have a property that will allow this to happen. What's the best way to set the focus from the VM?
I have created an IResult implementation that works quite well for achieving this. You can get the view from the ActionExecutionContext of the IResult, which you can then search (I search by name) for the control you want to focus.
public class GiveFocusByName : ResultBase
{
public GiveFocusByName(string controlToFocus)
{
_controlToFocus = controlToFocus;
}
private string _controlToFocus;
public override void Execute(ActionExecutionContext context)
{
var view = context.View as UserControl;
// add support for further controls here
List<Control> editableControls =
view.GetChildrenByType<Control>(c => c is CheckBox ||
c is TextBox ||
c is Button);
var control = editableControls.SingleOrDefault(c =>
c.Name == _controlToFocus);
if (control != null)
control.Dispatcher.BeginInvoke(() =>
{
control.Focus();
var textBox = control as TextBox;
if (textBox != null)
textBox.Select(textBox.Text.Length, 0);
});
RaiseCompletedEvent();
}
}
I have ommitted some extra code to get the view from the context when the view is a ChildWindow I can provide if you require.
Also GetChildrenByType is an extension method, here is one of many implementations available in the wild:
public static List<T> GetChildrenByType<T>(this UIElement element,
Func<T, bool> condition) where T : UIElement
{
List<T> results = new List<T>();
GetChildrenByType<T>(element, condition, results);
return results;
}
private static void GetChildrenByType<T>(UIElement element,
Func<T, bool> condition, List<T> results) where T : UIElement
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
UIElement child = VisualTreeHelper.GetChild(element, i) as UIElement;
if (child != null)
{
T t = child as T;
if (t != null)
{
if (condition == null)
results.Add(t);
else if (condition(t))
results.Add(t);
}
GetChildrenByType<T>(child, condition, results);
}
}
}
Your action would then be something like the following (invoked in Caliburn.Micro ActionMessage style).
public IEnumerable<IResult> MyAction()
{
// do whatever
yield return new GiveFocusByName("NameOfControlToFocus");
}
There is an easier way.
1º In the ViewModel add property _view as your UserControl
2º You must override OnViewLoaded of your ViewModel and
set _view to View object.
3º Set focus from any method.
public UserControlView _view { get; set; }
protected override void OnViewLoaded(object view)
{
base.OnViewLoaded(view);
_view = (UserControlView)view;
}
public void SetFocus()
{
_view.TextBox1.Focus();
}
I hope help you.

Categories

Resources