Programmatically select a specific TreeViewItem into an unbound TreeView - c#

In the code behind of my Silverlight application, I have the need to re-populate the TreeView and then make a specific TreeViewItem selected.
The code itself is pretty simple, here it is (i'll trim and pseudo-code-ify it to make it as short as possible)
private void Button_Click()
{
Guid idToSelect = TellMeWhatToSelect();
List<myObject> myDataList = myObjectRepository.RetrieveData().ToList();
myTreeView.Items.Clear();
foreach(myObject o in myDataList)
{
myTreeView.Items.Add(new TreeViewItem() { Content = o.DataField, Tag = o.Id });
}
myTreeView.Items.First(o => ((Guid)(o as TreeViewItem).Tag).Equals(idToSelect)).IsSelected = true;
}
That's basically it: i'm reading some data into myDataList, then i cycle through it and create as many TreeViewItems as needed in order to display the data.
Problem is, myTreeView.SelectedItem is null at the end of this, and SelectionChanged event isn't triggered. I would think that, since Items collection has been cleared and re-filled, switching IsSelected on one of the items would act like clicking, but it seems it doesn't).
Oddly enough (for me at least), issuing myTreeView.Items.First().IsSelected = true; by itself (that is, calling a method with that single line of code inside) works as expected: SelectedItem is there and all events are fired appropriateyl.
What's wrong with my code and/or what am I missing ? Looks like cleaning items up kind of breaks something.
I'm fairly sure others have had similar issues, but a bunch of searches I tried didn't help (most of the info & questions I came up with are WPF-related).
Thanks for your time, I'll provide more info if needed. Also, sorry for the wall of text.
UPDATE
Modifying code like this, now the method works as expected.
private void Button_Click()
{
Guid idToSelect = TellMeWhatToSelect();
List<myObject> myDataList = myObjectRepository.RetrieveData().ToList();
myTreeView.Items.Clear();
foreach(myObject o in myDataList)
{
myTreeView.Items.Add(new TreeViewItem() { Content = o.DataField, Tag = o.Id });
}
Dispatcher.BeginInvoke(()=>
{
myTreeView.Items.First(o => ((Guid)(o as TreeViewItem).Tag).Equals(idToSelect)).IsSelected = true;
});
}

Set property IsSelected inside of Dispatcher.BeginInvoke.

I had the same problem a while ago, I've solved by calling the UpdateLayout method from the treeview before setting the TreeViewItem as selected.Like this:
myTreeView.UpdateLayout();
myTreeView.Items.First(o => ((Guid)(o as TreeViewItem).Tag).Equals(idToSelect)).IsSelected = true;

Related

Why does my sort behavior fire with only one item in the list, and only the first time the collection is added to?

This question is a result of the fix to this problem. After getting the sort behavior to properly sort the ObservableCollection, I now notice that the first time the collection is added to, the CustomSorter handler fires with only the first item in it, and that same item is passed in as both parameters to the method. That is producing a duplicate item in the list.
Here are the pertinent parts of the view model code:
public ObservableCollection<PermissionItemViewModel> PermissionItems { get; private set; }
private void FetchRoleData()
{
PermissionItems.Clear();
if (SelectedRole != null)
{
using (var context = new myDataContext(new myDbFactory().GetConnectionString()))
{
foreach (PermissionsEnum permission in Enum.GetValues(typeof(PermissionsEnum)))
PermissionItems.Add(new PermissionItemViewModel(permission, SelectedRole[permission]));
}
}
}
All subsequent manipulations of that collection do not do this...it only happens the first time through the FetchRoleData method. Why?
EDIT:
Some additional information. The CustomSort property is set when the CollectionViewSource fires its Filter event (the only event it has AFAIK). I couldn't come up with any better trigger to get it set. The OnAttached override is too soon, as the View member of the CollectionViewSource is not set yet by that point. Catch 22, I guess. That is happening immediately after that first collection item is added. If this is due to the order in which things are being set, then are there any recommendations for a change?
I don't know how or where you're setting up the filter handler. Here's an example of how to set a custom sort on a CollectionViewSource when its View property changes. That's when you want to do it. This assumes that it's in the resources for a Window (or at least someplace where the Window can find it). But once you have cvs, wherever it comes from and however you got your mitts on it, the rest is the same.
public MainWindow()
{
InitializeComponent();
var cvs = FindResource("MyCollectionViewSource1") as CollectionViewSource;
var dpdView = DependencyPropertyDescriptor.FromProperty(
CollectionViewSource.ViewProperty, typeof(CollectionViewSource));
dpdView.AddValueChanged(cvs, (s, e) =>
{
if (cvs.View is ListCollectionView lvc)
{
lvc.CustomSort = new CustomSorter();
}
});
}
I'm baffled by your claim that the first item in the collection is being duplicated. No code you've shown, and no code I've given you, could have that effect. You'll have to share code that demonstrates that issue.

Binding to checked propert messes up ui, binding to text is fine

UPDATE:
I think i have found the problem.
All my forms events that affect the binding source have this in the end:
BndSource.ResetBindings(false);
If i comment this line in my CheckedChanged event handler, the issue stops. But why?
I have a very strange bug.
I have a class property:
public SqlByte AutomaticFlag { get; set; }
I wanted to use checkbox to facilitate for showing this so in initial inding i do this:
dtaAutomaticFlag.DataBindings.Add("Checked", BndSource, "AutomaticFlag", true);
dtaAutomaticFlag.DataBindings[0].Format += (s, e) =>
{
if ((SqlByte)e.Value == 1)
{
e.Value = true;
}
else
{
e.Value = false;
}
};
the problem is that during iteration through all records of the binding source my ui is half updated, meaning its not complete. See picture:
VERY strangely when i change the above binding property from checked to text like this:
dtaAutomaticFlag.DataBindings.Add("Text", BndSource, "AutomaticFlag", true);
the ui is ok!!
Picture:
I'm not sure if this applies to this particular situation. But rather than adding the binding as you did:
dtaAutomaticFlag.DataBindings.Add("Text", BndSource, "AutomaticFlag", true);
Does creating a "new" binding instance help at all?
dtaAutomaticFlag.DataBindings.Add(new Binding("Text", BndSource, "AutomaticFlag", true));

Copying a TabItem with an MVVM structure

This is an attempt to expand on this question. In my WPF program I've been cloning tabItems by using an XamlWriter in a function called TrycloneElement. I originally found this function here, but the function can also be viewed in the link to my previous question.
Now that I am beginning to worry about functionality inside my program, I found that the TrycloneElement function does not replicate any code-behind functionality assigned to the tabItem that it is cloning.
Because of High Core's link and comment on my earlier question I decided to start implementing functionality on my tabItems through Data Binding with my ViewModel.
Here is a sample of a command that I've implemented:
public viewModel()
{
allowReversing = new Command(allowReversing_Operations);
}
public Command AllowReversing
{
get { return allowReversing; }
}
private Command allowReversing;
private void allowReversing_Operations()
{
//Query for Window1
var mainWindow = Application.Current.Windows
.Cast<Window1>()
.FirstOrDefault(window => window is Window1) as Window1;
if (mainWindow.checkBox1.IsChecked == true) //Checked
{
mainWindow.checkBox9.IsEnabled = true;
mainWindow.groupBox7.IsEnabled = true;
}
else //UnChecked
{
mainWindow.checkBox9.IsEnabled = false;
mainWindow.checkBox9.IsChecked = false;
mainWindow.groupBox7.IsEnabled = false;
}
}
*NOTE: I know that I cheated and interacted directly with my View in the above code, but I wasn't sure how else to run those commands. If it is a problem, or there is another way, please show me how I can run those same commands without interacting with the View like I did.
Now to the question:
After changing my code and adding the commands to my ViewModel, the TrycloneElement function no longer works. At run time during the tab clone I receive an XamlParseException on line, object x = XamlReader.Load(xmlReader); that reads:
I'm fine with ditching the function if there is a better way and I don't need it anymore. But ultimately, how do I take a tabItem's design and functionality and clone it? (Please keep in mind that I really am trying to correct my structure)
Thank you for your help.
Revision of Leo's answer
This is the current version of Leo's answer that I have compiling. (There were some syntax errors)
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues) })
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd
where dpd != null
select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly)
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
Here is my example of a properly-implemented dynamic TabControl in WPF.
The main idea is that each Tab Item is a separate widget that contains its own logic and data, which is handled by the ViewModel, while the UI does what the UI must do: show data, not contain data.
The bottom line is that all data and functionality is managed at the ViewModel / Model levels, and since the TabControl is bound to an ObservableCollection, you simply add another element to that Collection whenever you need to add a new Tab.
This removes the need for "cloning" the UI or do any other weird manipulations with it.
1.) To fix that XamlParseException, make sure you have a public constructor like an empty one, you probably defined a constructor and when you tried to serialize that object and deserialize it can't. You have to explicitly add the default constructor.
2.) I don't like the word clone, but I'd say, when they want to copy. I'll manually create a new tab item control then do reflection on it.
I have this code that I made
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] {new PropertyFilterAttribute(PropertyFilterOptions.SetValues)})
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd where dpd != null select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly))
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
So it would be like
var newTabItem = new TabItem();
newTabItem.CopyPropertiesFrom(masterTab);

C# Dynamic form (reflection) - linking controls

Sorry for the poor quality of the title. I couldn't think of a better way to phrase this.
For a project I'm currently working on with a few friends, I got myself in the situation where I have created a dynamic form (with reflection) which I now want to validate.
Example (ignore the black box, it contains old form elements which are now irrelevant and i didn't want to confuse you guys):
As you may have guessed already, it is an application for creating a mysql database.
Which is where I get to my problem(s). I want to disable checkboxes if others are checked.
For example: If I check "PrimaryKey" I want to disable the checkbox "Null".
Changing from unsigned to signed changes the numericupdown minimum and maximum etc.
But with reflection and all, I find it difficult to know exactly which checkbox to disable.
I was hoping you guys would have some suggestions.
I have been thinking about this for a while and a few thoughts have come to mind. Maybe these are better solutions than the current one.
Thought 1: I create UserControls for every datatype. Pro's: no problems with reflection and easy identifying of every control in the UserControl for validation. Con's: Copy-Pasting, Lots of UserControls, with a lot of the same controls.
Thought 2: Doing something with the description tags for every property of the classes. Creating rules in the description that allow me to link the checkboxes together. Here I'll only have to copy the rules to every class property and then it should be ok.
I had been thinking of other solutions but I failed to remember them.
I hope you guys can give me a few good pointers/suggestions.
[Edit]
Maybe my code can explain a bit more.
My code:
PropertyInfo[] properties = DataTypes.DataTypes.GetTypeFromString(modelElement.DataType.ToString()).GetType().GetProperties();
foreach (PropertyInfo prop in properties)
{
if (prop.Name != "Label" && prop.Name != "Project" && prop.Name != "Panel")
{
var value = prop.GetValue(modelElement.DataType, null);
if (value != null)
{
tableLayoutPanel1.Controls.Add(new Label { Text = prop.Name, Anchor = AnchorStyles.Left, AutoSize = true });
switch (value.GetType().ToString())
{
case "System.Int32":
NumericUpDown numericUpDown = new NumericUpDown();
numericUpDown.Text = value.ToString();
numericUpDown.Dock = DockStyle.None;
tableLayoutPanel1.Controls.Add(numericUpDown);
break;
case "System.Boolean":
CheckBox checkBox = new CheckBox();
checkBox.Dock = DockStyle.None;
// checkbox will become huge if not for these changes
checkBox.AutoSize = false;
checkBox.Size = new Size(16, 16);
if (value.Equals(true))
{
checkBox.CheckState = CheckState.Checked;
}
tableLayoutPanel1.Controls.Add(checkBox);
break;
default:
MessageBox.Show(#"The following type has not been implemented yet: " + value.GetType());
break;
}
}
}
}
Here is a mockup from my comments:
// The ViewModel is responsible for handling the actual visual layout of the form.
public class ViewModel {
// Fire this when your ViewModel changes
public event EventHandler WindowUpdated;
public Boolean IsIsNullCheckBoxVisible { get; private set; }
// This method would contain the actual logic for handling window changes.
public void CalculateFormLayout() {
Boolean someLogic = true;
// If the logic is true, set the isNullCheckbox to true
if (someLogic) {
IsIsNullCheckBoxVisible = true;
}
// Inform the UI to update
UpdateVisual();
}
// This fires the 'WindowUpdated' event.
public void UpdateVisual() {
if (WindowUpdated != null) {
WindowUpdated(this, new EventArgs());
}
}
}
public class TheUI : Form {
// Attach to the viewModel;
ViewModel myViewModel = new ViewModel();
CheckBox isNullCheckBox = new CheckBox();
public TheUI() {
this.myViewModel.WindowUpdated += myViewModel_WindowUpdated;
}
void myViewModel_WindowUpdated(object sender, EventArgs e) {
// Update the view here.
// Notie that all we do in the UI is to update the visual based on the
// results from the ViewModel;
this.isNullCheckBox.Visible = myViewModel.IsIsNullCheckBoxVisible;
}
}
The basic idea here is that you ensure that the UI does as little as possible. It's role should just be to update. Update what? That's for the ViewModel class to decide. We perform all of the updating logic in the ViewModel class, and then when the updating computations are done, we call the UpdateVisual() event, which tells the UI that it needs to represent itself. When the WindowUpdated Event occurs, the UI just responds by displaying the configuration set up by the ViewModel.
This may seem like a lot of work to set up initially, but once in place it will save you tons and tons of time down the road. Let me know if you have any questions.
Try relating the event of one checkbox to disable the other; something like this:
private void primaryKeyBox_AfterCheck(object sender, EventArgs e)
{
nullBox.Enabled = false;
}
This is a very simple example and would have to be changed a bit, but for what I think you're asking it should work. You would also have to add to an event for the boxes being unchecked. You would also need logic to only get data from certain checkboxes based on the ones that are and are not checked.
For all the other things, such as changing the numbers based on the dropdown, change them based on events as well.
For WinForms I would use data binding.
Create an object and implement INotifyPropertyChanged and work with that object.
Then, If you have an object instance aObj:
To bind the last name property to a textbox on the form do this:
Private WithEvents txtLastNameBinding As Binding
txtLastNameBinding = New Binding("Text", aObj, "LastName", True, DataSourceUpdateMode.OnValidation, "")
txtLastName.DataBindings.Add(txtLastNameBinding)
Take a look here for more info.
INotifyPropertyChanged

How can I make a specific TabItem gain focus on a TabControl without click event?

How can I tell my TabControl to set the focus to its first TabItem, something like this:
PSEUDO-CODE:
((TabItem)(MainTabControl.Children[0])).SetFocus();
How about this?
MainTabControl.SelectedIndex = 0;
this.tabControl1.SelectedTab = this.tabControl1.TabPages["tSummary"];
I've found it's usually a best practice to name your tabs and access it via the name so that if/when other people (or you) add to or subtact tabs as part of updating, you don't have to go through your code and find and fix all those "hard coded" indexes. hope this helps.
I realise this was answered a long time ago, however a better solution would be to bind your items to a collection in your model and expose a property that selected item is bound to.
XAML:
<!-- MyTemplateForItem represents your template -->
<TabControl ItemsSource="{Binding MyCollectionOfItems}"
SelectedItem="{Binding SelectedItem}"
ContentTemplate="{StaticResource MyTemplateForItem}">
</TabControl>
Code Behind:
public ObservableCollection<MyItem> MyCollectionOfItems {
get;
private set;
}
private MyItem selectedItem;
public MyItem SelectedItem{
get { return selectedItem; }
set {
if (!Object.Equals(selectedItem, value)) {
selectedItem = value;
// Ensure you implement System.ComponentModel.INotifyPropertyChanged
OnNotifyPropertyChanged("SelectedItem");
}
}
}
Now, all you have to do to set the item is:
MyItem = someItemToSelect;
You can use the same logic with the SelectedIndex property, further, you can use the two at the same time.
This approach allows you to separate your model correctly from the UI, which could allow you to replace the TabControl with something else down the line but not requiring you to change your underlying model.
Look at the properties for the tab control...
Expand the TabPages properties "collection"...
Make note of the names you gave the members.
ie. a tab control called tabMain with 2 tabs called tabHeader and tabDetail
Then to select either tab...You have to set it with the tabname
tabMain.SelectedTab = tabHeader;
tabControl1.SelectedTab = item;
item.Focus();
Basically all of the answers here deal with SELECTION, which does not answer the question.
Maybe that is what OP wanted, but the question very specifically asks for FOCUS.
TabItem item = (TabItem)MainTabControl.Items[0];
// OR
TabItem item = (TabItem)MainTabControl.SelectedItem;
// Then
item.Focus();
tabControl.SelectedItem = tabControl.Items[0];
If you have a Tabcontroller named tabControl you could set the selectedIndex from different methods, i use following methods mostly.
codebehind:
tabControl.SelectedIndex = 0; // Sets the focus to first tabpanel
clientside:
First, put the following javascript in your aspx/ascx file:
<script type="text/javascript">
function SetActiveTab(tabControl, activeTabIndex) {
var activeTab = tabControl.GetTab(activeTabIndex);
if(activeTab != null)
tabControl.SetActiveTab(activeTab);
}</script>
Then add following clientside event to prefered controller:
OnClientClick="function(s, e) { SetActiveTab(tabControl, 0);
it's better to use the following type of code to select the particular
item in the particular tab...
.
private void PutFocusOnControl(Control element)
{
if (element != null)
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input,
(System.Threading.ThreadStart)delegate
{
element.Focus();
});
}
And in calling time... tabcontrol.isselected=true;
PutFocusOnControl(textbox1);
will works fine...
Private Sub TabControl1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles TabControl1.SelectedIndexChanged
'MsgBox(TabControl1.SelectedIndex)
If TabControl1.SelectedIndex = 0 Then
txt_apclntFrstName.Select()
Else
txtApplcnNo.Select()
End If
End Sub
It worked for me to set focus to the last tab just after I open it:
//this is my assignment of the collection to the tab control
DictTabControl.DataContext = appTabs.DictTabs;
//set the selected item to the last in the collection, i.e., the one I just added to the end.
DictTabControl.SelectedItem = DictTabControl.Items[(DictTabControl.Items.Count-1)];

Categories