Accessing UserControl when it's a DataTemplate in a ListBox - c#

Fist time question long time user of Stack Overflow. I'm somewhat new to the whole MVVM space so I think my Google Fu is failing me. I apologize if I get some of these terms incorrect.
I currently have an observable list class:InstrumentExecutor.
This list is then set as the ItemsSource of a ListBox called InstrumentList.
The ListBox is setup to use a DataTemplate with a FrameworkElementFactory that uses the InstrumentControl UserControl to display the data.
DataTemplate code:
DataTemplate instrumentDataTemplate = new DataTemplate();
instrumentDataTemplate.DataType = typeof(InstrumentExecutor);
FrameworkElementFactory frameelement = new FrameworkElementFactory(typeof(InstrumentControl));
instrumentDataTemplate.VisualTree = frameelement;
InstrumentList.ItemTemplate = instrumentDataTemplate;
And that is all working just great! Data is flowing in and out and everyone is happy. Here's the part where I'm having trouble.
In the user control there is some Expander WPF code. I am wanting to send an event, call a method, do something to trigger the expansion or collapse from the parent ListBox to each and every item in this listbox. Whenever I iterate the InstrumentList.Items the data source (InstrumentExecutor) is the only data I see.
There's some ways I can hack around using properties and notifications in the data class (Instrument Executor) but I'd like to see if I can make it cleaner.
So is there a way I can access each UserControl? I've looked at ItemContainerGenerator but haven't had much luck.
Thanks for the help!
Update 1: Using the VisualTreeHelper idea and some StackOver during the usercontrol load event for each line item I can find the parent (code below) and build a linkage that way. Here's the code that goes with this idea.
public static T FindParent<T>(DependencyObject current) where T : class
{
DependencyObject parent = VisualTreeHelper.GetParent(current);
if (parent == null)
return null;
if (parent is T)
return parent as T;
else
return FindParent<T>(parent);
}
It works but it's not as sexy as I'm used to. Next up is trying the Binding idea. Thanks!

Related

WPF: Button if clicked runs a new query and updates the DataGrid with the new query

What I want to do is access the database via a query (already have one made, but heres the issue:
namespace WpfApp3
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class ViewData : Window
{
public ViewData()
{
InitializeComponent();
}
private void ShowData(object sender, RoutedEventArgs e)
{
CustomerEntities1 db = new CustomerEntities1();
if (report.IsInitialized)
{
var report = from values in db.Customers
select values;
valueGrid.ItemsSource = report.ToList();
}
else if (sortName.IsInitialized)
{
var sortName = from values in db.Addresses
select values;
valueGrid.ItemsSource = sortName.ToList();
}
}
}
}
I Do not understand how the bindings in WPF work, and Im having issues when running the code. It only runs the top portion of the if condition. Any ideas what binding I should use where if clicked, it is considered true, and runs the condition when met. Once the condition is met, it should remove the old one, and replace it with the new query for the DataGrid. Thank You
So in WPF the UI Components (valueGrid) in your case are usually notified if the Data changes (CustomerEntities1) on Initialization and IF and ONLY IF the Source implements INotifyPropertyChanged . you should therefore consider implementing this interface in ally or ViewModels, assuming you are using the MVVM pattern.
Any ideas what Binding I should use
The way your code is set up you can't use bindings at all but you would want a oneWay binding if all you do is populate your DataGrid from code.
If you want to use Data binding you need to set the DataContext of your Window to something like
DataContext = this;
This will allow you to bind properties that you declare in your code behind to UI controls in your .xaml code like this:
<TextBlock Text="{Binding PropertyName}"/>
Now this might work syntax wise but your DataGrid still won't update because your DataContext class or any of their parent classes need to Implement the INotifyPropertyChanged Interface.
I advise you to start reading about the basics of Data binding and work through a tutorial or two. Because even if you follow what I've written above and you get it to work it is not the ideal approach to this problem.
You'd want to set up a type of class known as ViewModel that will be have all the properties that hold the data you want to show in your View (the Window) and have it as the Datacontext.
This link will give you a good overview of what data binding is, what you can do and how get it to work.
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/?view=netdesktop-6.0

Fire event from ManWindow in selected tab

I'm sorry if this question was asked before.
After 1 hour of searching here i could not to find it.
First, i'm using WPF, but not with MVVM. I know MVVM is way to go and i'm learning it. I'm new to programming.
It's small program and i have buttons on mainwindow in one StackPanel, and TabControl (_tabcntrl) in another.
On button click mainwindow generates one tab:
TabItem _tab = new TabItem();
UserControl _uc = new UserControl();
_tab.Content = _uc;
_tabcntrl.Items.Add(tab);
In usercontrol i have one public event
public void test()
{
//some code
}
So my question is how to fire this event from main window (button click in main window), but only in selected tab. Idea is that you can have multiple tabs with same usercontrol.
I know i can do it with
_uc.test();
But only when tab is created.
Also i tried to put
TabItem tb = _tabcntrl.SelectedItem;
tb.test();
In button click event, but i get error.
Stupid thing is that i figured out how to fire event from usercontrol, and i can't other way around. Feeling pretty stupid for asking this in first place.
Thank You, sorry for my bad English
There are various options, you can either look for VisualTree or simply use Children property of the Control and find respective element.
TabItem tb = _tabcntrl.SelectedItem;
var childControls = control.Children.OfType<UserControl>(); // your controltype
// I'm looping through all child controls of type 'UserControl' but you can customize to your case.
foreach(var control in childControls)
{
// execute control logic here
control.test();
}

preselect checkboxes in radtreeview silverlight)

I have a silverlight app where there is a telerik radtreeview with checkboxes. The user selects stuff and when the user wants to edit it's selection i need to pre-populate the tree with the previously saved selection.
I found out that I can bind the checkboxes to my viewmodel. But if I choose that scenario I don't use the "built in" checkboxes and lose the tristate logic (autoselecting siblings when selecting a parent and such)
So I am experimenting with trying to get the radtreeviewitem objects from the radtreeview.items collection
http://www.telerik.com/help/silverlight/radtreeview-how-to-iterate-through-treeviewitems.html
The problem is that the radtreeviewitems are only generated when a node is expanded by a user in the ui. So not all items I want to iterate through are present after the control is databound.
I have not found a good way to force the ui to build all the radtreeviewitems so I can iterate through them and set my preselection. I found the links below but it only seems to work with the root node, not the siblings.
WPF: control.ItemContainerGenerator.Status is NotStarted. How do I tell it to start now?
Would you guys also consider rebuilding the "tristate-mode" into your viewmodel logic "dirty"?
How would you go about preselecting checkboxitems in the radtreeview?
This is how I do it :
public static void CheckAllTreeItemsAuto(RadTreeView tree)
{
tree.ItemContainerGenerator.StatusChanged += (s, e) =>
{
if ((s as Telerik.Windows.Controls.ItemContainerGenerator).Status == Telerik.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
RadTreeViewItem item = (RadTreeViewItem)tree.ItemContainerGenerator.ContainerFromIndex(0);
while (item != null)
{
item.IsChecked = true;
item = item.NextItem;
}
}
};
}
I didn't experience your problem with the items not generated at the start. (I don't know how you generate your RadTreeView).
When working with the RadTreeView control you need to have in mind that the built-in tri-state logic is designed to work with declaratively defined control and items, only. This means that using this feature in MVVM scenarios will not work as expected.
Since Telerik is aware of that limitation they provided the community with an article demonstrating how developers can use the tri-state logic of a native CheckBox control in MVVM scenarios. You can find the article in their documentation. Also, at the end of the article you can find a link leading to their CodeLibrary where you can download ready to run project demonstrating the described approach.
I hope this information will help you.

WPF Listbox remove selection by clicking on blank space

I have a wpf listbox with a custom item template which contains a rectangle.
The each item in the listbox can be selected (only one at a time).
I want to add a behavior in which when a user clicks on a place which isn't the item (for instance, a blank spot on the listbox, which is not an item), the selected item will become deselected.
Any ideas?
Thanks.
For example with a simple listbox:
item 1
item 2
The behavior that I'm looking for is when the user clicks on pixel 500 (which is a part of the listbox but not on an item), the currently selected item will be deselected.
The simple solution is to data bind a property to the ListBox.SelectedItem property and set it to null whenever you want to clear the selection:
<ListBox ItemsSource="{Binding YourItems}" SelectedItem="{Binding SelectedItem}"
SelectionMode="Single" />
Then in code, you can just do this to clear the selection:
SelectedItem = null;
And when would you do that? You can attach a handler to the PreviewMouseLeftButtonDown event of the Window, or any other control in your UI. In the handler method, you could do a hit test to see what the item the user clicked on was:
HitTestResult hitTestResult =
VisualTreeHelper.HitTest(controlClickedOn, e.GetPosition(controlClickedOn));
Control controlUnderMouse = hitTestResult.VisualHit.GetParentOfType<Control>();
See the VisualTreeHelper.HitTest Method (Visual, Point) for more help with this part.
Then maybe something like this:
if (controlUnderMouse.GetType() != typeof(ListBoxItem)) SelectedItem = null;
Of course, there are many ways to do this, and you'll have to fill in the few blank spots that I left, but you should get the idea.
EDIT >>>
The generic GetParentOfType method is a custom Extension Method that is defined in a separate class named DependencyObjectExtensions:
public static class DependencyObjectExtensions
{
public static T GetParentOfType<T>(this DependencyObject element)
where T : DependencyObject
{
Type type = typeof(T);
if (element == null) return null;
DependencyObject parent = VisualTreeHelper.GetParent(element);
if (parent == null && ((FrameworkElement)element).Parent is DependencyObject)
parent = ((FrameworkElement)element).Parent;
if (parent == null) return null;
else if (parent.GetType() == type || parent.GetType().IsSubclassOf(type))
return parent as T;
return GetParentOfType<T>(parent);
}
...
}
For The each item in the listbox can be selected (only one at a time).
You can come up with one of followings
1- Disable the item after it is selected.
2- Maintain a list at backend to mark each index selectable or unselectable.
To assure that only one item is selected put this in the listbox:
SelectionMode="Single"
then for the unselect when clicking somewhere, you can try to check this events
PreviewMouseLeftButtonUp
LostFocus()
Regards,
I was searching for a solution to this problem, but to prevent the last item in the listbox from becoming selected when clicking on the blank space. my problem is slightly different but has the same solution that I have come up with which works for me.
Although I am using powershell and not c#, I am still utilizing the windows forms listbox control so I think the idea will be applicable.
Also, I couldn't find any discussions of this problem specifically dealing with powershell when I was searching for a solution, so I wound up here.
so I created a variable, maxY, and multiplied the number of list items by the itemheight.
next, at the beginning of the mouse_up event, I just check if the Y location from the mouse click is less than the maxY variable. if true, select the item and run your code, if not, do nothing.
I can only provide a code sample in powershell, but I think the idea is portrayed.
$listbox.add_MouseUP({
$maxY = $this.items.count * $this.itemHeight
if ($_.y -le $maxY) {
$this.SelectedIndex = $this.IndexFromPoint($_.X, $_.y)
#do stuff here
}
else {
$this.clearselection()
}
}
This will clear all selections if clicking on blank space, but will also prevent an item from being selected when clicking on blank space.

Shifting nodes up and down in a TreeView

In my program I have a UserControl that contains a TreeView. That TreeView has a ViewModel and a Model relating to it. I would like to make it so that by clicking buttons, I can shift nodes up and down throughout the tree. This is similar to what one might implement on a listBox.
As a guide, I am using this article.
I am implementing the following functions into the code-behind of the UserControl for which the TreeView exists.
//Move up
private void moveUp_Click(object sender, RoutedEventArgs e)
{
if(UCViewModel.TreeView.SelectedItem != null)
{
if(UCViewModel.TreeView.SelectedItem is TreeModel)
{
TreeModel tm = UCViewModel.TreeView.SelectedItem as TreeModel;
if(tm.Rank != 1)
{
}
}
}
}
private void MoveUp(TreeModel tm)
{ //My guess on how to call the equivalent command...
foreach (TreeModel item in // **UCViewModel.TreeView.GetAllChildren....? )
{
}
}
Because my structure is different, and I am actually implementing an ObservableCollection as a TreeView, I do not have access to the same methods as the code in the example.
The following lines are the lines that I am concerned about...
TreeView.Items();
TreeView.Items.Clear();
TreeView.Items.Add();
How can I make the equivalent calls with the way my TreeView is setup? Please let me know if more code would be helpful.
The main idea of MVVM is not to use anything like treeView.Items.Add() or treeView.GetAllChildren() or whatever method you need from TreeView.
MVVM Pattern says you dont care about View and you dont know about the View or any control inside the View.
Therefore if you have an ObservableCollection as ItemsSource in your ViewModel you just need to move items there and the TreeView will follow you.
As simple as that. Your TreeView just needs to know where the ObservableCollection is placed inside your ViewModel.
Whenever you change something inside ObservableCollection you trigger collection changed event with appropriate event arguments holding information whether you added new items or shifted items around. That is how TreeView will know what to do.

Categories

Resources