I am new to WPF, and I want to do something when a user switches between my tabcontrol items.
As expected, I had the issue of firing the selectionchanged event multiple times, then I read this post:
In C# WPF, why is my TabControl's SelectionChanged event firing too often?,
and I don't like the first solution which requires too many extra code for handling event for each selectors in the application. Hence, I tried the solution in this post:
TabControl's SelectionChanged event issue,
but I got a new issue that I couldn't find any related post in stackoverflow.
The problem I have is that the following code doesn't return true:
if (e.Source is TabControl){ // do something }
neither this one:
if (e.Source is TabItem) {// do something}
When I hover on the e.Source in debug mode, it shows as
{System.Windows.Controls.TabControl Items.Count:5}
and if I tried to view it in WPF Tree Visualizer, it tells me that it is the TabControl that I expected for.
So my question is, why it doesn't return true since it is a TabControl?
Here is my code for SelectionChanged:
void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.Source is TabControl)
{
if (item1.IsSelected)
{
myllist1.DataContext = getList1();
}
else if (item2.IsSelected)
{
mylist2.DataContext = getlist2();
}
else if (item3.IsSelected)
{
mylist3.DataContext = getlist3();
}
else if (item4.IsSelected)
{
mylist4.DataContext = getlist4();
}
}
}
You have to convert e.source from an Object to a FrameworkElement, and then compare the types.
if (((FrameworkElement)e.Source).GetType()== typeof(System.Windows.Controls.TabControl))
{
if (item1.IsSelected)
{
myllist1.DataContext = getList1();
}
else if (item2.IsSelected)
{
mylist2.DataContext = getlist2();
}
else if (item3.IsSelected)
{
mylist3.DataContext = getlist3();
}
else if (item4.IsSelected)
{
mylist4.DataContext = getlist4();
}
}
Related
I have a combobox with a custom enum (just true/false). I have a function that checks conditions if the SelectedValue changes from false to true and if the conditions are wrong it changes the combobox SelectedValue back to false. This changes the SelectedValue to false if you check it in code, but when you look at the UI it's still on true.
Here's the xaml for the combobox:
<ComboBox x:Name="comboEnabled1" Width="80" Height="26"
ItemsSource="{Binding Path=TrueFalseChoices}"
SelectedValue="{Binding Path=Enable1, Mode=TwoWay}"/>
Here's the viewmodel
private TrueFalse _enable1 = TrueFalse.False;
public TrueFalse Enable1
{
get { return _enable1; }
set
{
if (_enable1 != value)
{
_enable1 = value;
base.OnPropertyChanged("Enable1");
OnEnableChanged(EventArgs.Empty);
}
}
}
And here's the function that I'm using to check the conditions
public void HandleEnable(object sender, EventArgs e)
{
if(Enable1 == TrueFalse.True)
{
if(!connected)
{
HandleMessage("Can't enable, not connected");
Enable1 = TrueFalse.False;
}
else if (!_main.CBCheck(_main.cbReason))
{
Enable1 = TrueFalse.False;
}
}
Console.WriteLine("Enabled {0}", Enable1);
}
Was thinking I'm changing the value too rapidly, but the last Console.Writeline produces the right outcome each time.
Any help appreciated!
Edit: Calling Handleenable here:
protected void OnEnableChanged(EventArgs e)
{
EventHandler handler = EnableChanged;
if (handler != null)
handler(this, e);
}
And in the ViewModel funct:
EnableChanged += HandleEnable;
Changing the Enable1 in any other place worked as it should have, only having issues in HandleEnable function.Also tried changing other comboboxes in the HandleEnable function and that worked as it should have.
I would recommend actually disabling the ComboBox if the requirements are not met.
But if you insist on reverting Enable1 back to False if conditions are not met, you should push the notification properly through the dispatcher.
set
{
var effectiveValue = condition ? value : TrueFalse.False;
if (effectiveValue == TrueFalse.False && value == TrueFalse.True)
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(
new Action(() => base.OnPropertyChanged("Enable1"), null));
//your regular set-code follows here
}
It happens because WPF is already responding to that event, and therefore ignoring the subsequent calls until it's done. So you immediately queue another pass as soon as the current one is finished.
But I would still recommend disabling the ComboBox when it is effectively disabled. Accessing the dispatcher from a viewmodel does not smell good no matter how you look at it.
UPD: You can also solve that with {Binding Enable1, Delay=10} if your framework is 4.5.1+.
I am using the TreeView from the WinrtXamlToolkit. The default behavior of this control is to expand the nested items on double click of the header. The code responsible for this is here (TreeViewItem.cs line 1205).
private void OnHeaderMouseLeftButtonDown(object sender, PointerRoutedEventArgs e)
{
if (Interaction.AllowMouseLeftButtonDown(e))
{
// If the event hasn't already been handled and this item is
// focusable, then focus (and possibly expand if it was double
// clicked)
if (!e.Handled && IsEnabled)
{
if (Focus(FocusState.Programmatic))
{
e.Handled = true;
}
// Expand the item when double clicked
if (Interaction.ClickCount % 2 == 0)
{
bool opened = !IsExpanded;
UserInitiatedExpansion |= opened;
IsExpanded = opened;
e.Handled = true;
}
}
Interaction.OnMouseLeftButtonDownBase();
OnPointerPressed(e);
}
}
Is there a way to change this behavior to expand the items on single click or tap without actually copying the control and all it's related classes to my project?
It seems like an overkill to do this just to change a few lines of code.
I tried to do drag'n'drop stuff with that TreeView and was in a similar situation. My first move was to actually copy all the TreeView and its related classes and man there are a lot. There's a lot of internal stuff happening and I pretty much gave up interfering with it after a bunch of other stuff stopped working.
So my solution was to just have a specific control inside the ItemTemplate that handled dragging for me. For you this would be a Button whose Click you handle. In the eventhandler you will navigate up the visual tree to your TreeViewItem and change the IsExpanded.
I have a UserControl which I am loading into a div which is inside an UpdatePanel. Here is my code for loading it:
controls.IDLControl IdlControl = LoadControl(#"~/controls/IDLControl.ascx") as controls.IDLControl;
IdlControl.ClientIDMode = ClientIDMode.Static;
IdlControl.ID = "IDLControl";
spGroup.Controls.Clear();
spGroup.Controls.Add(IdlControl);
And here is my code for trying to retrieve an instance of it:
controls.IDLControl IdlControl = RecursiveFindControl(this, "IDLControl") as controls.IDLControl;
private Control RecursiveFindControl(Control targetControl, string findControlId) {
if (targetControl.HasControls()) {
foreach (Control childControl in targetControl.Controls) {
if (childControl.ID == findControlId) {
return childControl;
}
RecursiveFindControl(childControl, findControlId);
}
}
return null;
}
But, all I get is null. I need help on figuring this out.
AFAIK, I need to re-add the control to the page on pre-init but it is one of the controls that can be added depending on which option is selected from a drop down list (which also is filled dynamically). I am stuck trying to figure out how to make this work.
You can try something like this to add your control back in the Page_Init based on the option selected in your DropDownList.
protected void Page_Init(Object sender, EventArgs e)
{
if (IsPostBack)
{
if (drpYourDropDown.Items.Count > 0 && drpYourDropDown.SelectedItem.Text == "yourOption")
{
AddIDLControl();
}
}
}
private void AddIDLControl()
{
controls.IDLControl IdlControl = LoadControl(#"~/controls/IDLControl.ascx") as controls.IDLControl;
IdlControl.ClientIDMode = ClientIDMode.Static;
IdlControl.ID = "IDLControl";
spGroup.Controls.Clear();
spGroup.Controls.Add(IdlControl);
}
my code dynamically adding ListBoxes, which contains buttons. Buttons dynamically addind another buttons. Well... thats expectation. My code doesn't works.
Here is my code:
public Button createElements(string nameOfElement)
{
if (nameOfElement.Contains("Floor"))
{
// code creating Button
return floorButton;
}
else if (nameOfElement.Contains("Sound"))
{
// code creating Button
return soundButton;
}
else if (nameOfElement.Contains("Add"))
{
// code creating Button
return addButton;
}
return null;
}
private ListBox addNewListBox(ListBox targetElement, int ex)
{
// vytvori ListBox do hlavniho ListBoxu
ListBox elementListBox = new ListBox();
elementListBox.Name = "elementListBox" + indexY;
elementListBox.VerticalAlignment = VerticalAlignment.Top;
elementListBox.ItemsPanel = (ItemsPanelTemplate)XamlReader.Parse("<ItemsPanelTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"><StackPanel Orientation=\"Horizontal\"/></ItemsPanelTemplate>");
if (ex == 1)
{
targetElement.Items.Remove(addFloor);
targetElement.Items.Add(elementListBox);
elementListBox.Items.Add(createElements("Floor"));
elementListBox.Items.Add(createElements("Sound"));
elementListBox.Items.Add(createElements("Add"));
targetElement.Items.Add(addFloor);
indexY++;
indexX = 0;
}
return elementListBox;
}
Here is detail of final function.
private void putElements(ListBox targetElement, Button targetObject)
{
targetElement.Items.Add(targetObject);
// there's problem
MessageBox.Show("targetElement: ", targetElement.Name);
MessageBox.Show("targetObject", targetObject.Name);
}
Click event calling this function:
putElements(addNewListBox(mainListBox, 0), createElements("Sound"));
MessageBoxes prints names of objects in last function. Objects are right, but this line:
targetElement.Items.Add(targetObject);
there is problem - this line doing nothing..
Thanks for help!!
You're just adding the new ListBox to your mainListBox if the ex-value is 1, but in your case it's 0.
So your new ListBox (targetElement in putElements-method) will never be added to your mainListBox and hence not displayed. Thats why targetElement.Items.Add(targetObject); seems to not work.
I have a DataGrid view1 and a ListView and when ever I select the list view item(I am passing the ListView item into the query and populating the DataGrid view according that item)
I have wrote some code like this....
private void listview_selectedindexchanged(object sender event args)
{
if (listview.SelectedItems.Count > 0 && listview.SelectedItems[0].Group.Name == "abc")
{
if(lstview.SelectedItems[0].Text.ToString() == "sfs")
{
method1();
}
else
{
// datagrid view1 binding
blah.....
}
}
if (lstview.SelectedItems.Count > 0 && lstview.SelectedItems[0].Group.Name == "def")
{
if(lstview.SelectedItems[0].Text.ToString() == "xyz")
{
method 1();
}
if(lstview.SelectedItems[0].Text.ToString() == "ghi")
{
method 2(a,b);
}
if(lstview.SelectedItems[0].Text.ToString() == "jkl")
{
method 2(c,d);
}
if(lstview.SelectedItems[0].Text.ToString() == "mno")
{
method 3();
}
}
}
private void method 1()
{
// datagrid view1 binding
blahh
}
private void method 2(e,g)
{
// datagrid view1 binding
blah....blah..
}
private void method 3()
{
// datagrid view1 binding
}
I have done it like above ... I think this is not an efficient way to do the coding. and this code consisits of a lot of repeated lines, is there any way to refractor this code to a small bunch of code ......
in order improve the efficiency?
Any ideas and sample snippets for increasing code efficiency would be helpful to me ...
Many thanks in advance....
I am using c# and writting WinForms applications.....
You could save a delegate into the listview item. And call it when the encapsulating item gets selected. For example you would fill your listbox like this:
ListViewItem item = new ListViewItem("abc");
item.Tag = new Delegate(method1);
lstview.Items.Add(item);
Now, when this item gets selected, you execute the method like so:
private void listview_selectedindexchanged(object sender event args)
{
((Delegate)lstview.SelectedItems[0].Tag)(); // this will execute method1 if the item with text "abc" gets selected
}
NOTE: ! have not tested this code, but something along those lines should work and you don't have to write the If-statement, you only have to construct the items correctly.
Also note that this may be a bit hard to read for someone new to this code.
You could easily extract a new method to do the "datagrid view1 binding". This method is then called from all the methods that need to do the binding.