I have small problem with retrieving the state of a canvas' visibility property. When I retrieve the page state, the canvas is always visible even if it was collapsed when it was tombstoned. I tried a bunch of if else and switch statements but with no luck. How do I fix this bug? Thanks in advance to anyone who wants to help!
Here's the code:
private const string coachPivotKey = "CoachPivotKey";
private const string isVisibleKey = "IsVisibleKey";
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
this.SaveState(coachPivotKey, coachPivot.SelectedIndex);
this.SaveState(isVisibleKey, canvasNotes.Visibility);
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
coachPivot.SelectedItem = coachPivot.Items[this.LoadState<int>(coachPivotKey)];
canvasNotes.Visibility = this.LoadState<Visibility>(isVisibleKey);
base.OnNavigatedTo(e);
}
The LoadState() and SaveState() methods are in a different class. These I got from a video I watched on tombstoning:
public static void SaveState(this PhoneApplicationPage phoneApplicationPage, string key, object value)
{
if (phoneApplicationPage.State.ContainsKey(key))
{
phoneApplicationPage.State.Remove(key);
}
phoneApplicationPage.State.Add(key, value);
}
public static T LoadState<T>(this PhoneApplicationPage phoneApplicationPage, string key)
{
if (phoneApplicationPage.State.ContainsKey(key))
{
return (T)phoneApplicationPage.State[key];
}
return default(T);
}
Instead of saving a System.Windows.Visibility, save a bool indicating whether the control is visible.
this.SaveState(isVisibleKey,coachNotes.Visibility == Visibility.Visible);
canvasNotes.Visibility = this.LoadState<bool>(isVisibleKey) ? Visibility.Visible : Visibility.Collapsed;
Related
I created an ObservableCollectionEx.cs class that inherits the ObservableCollection class to suppress notifications while the collection is being updated until it's done updating from the answer here.
The class:
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
private bool _notificationSupressed = false;
private bool _supressNotification = false;
public bool SupressNotification
{
get
{
return _supressNotification;
}
set
{
_supressNotification = value;
if (_supressNotification == false && _notificationSupressed)
{
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
_notificationSupressed = false;
}
}
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (SupressNotification)
{
_notificationSupressed = true;
return;
}
base.OnCollectionChanged(e);
}
}
A collection of models is created in a class that is meant to update in response to a number of events. One is based on an observable sequence that simply updates the collection at an interval and another is based on a button click event. Stepping through the codes, I see that both events are causing the collection to update successfully, but only the button click causes the WPF ListView to be notified and updated accordingly. The UI is a WPF UserControl that is used to create a CustomTaskPane in Microsoft Word using VSTO.
The code that updates the collection via Observable sequence:
public partial class CrossReferenceControl : UserControl, ICrossReferenceControl
{
private ICrossReferenceControlViewModel referenceControlViewModel;
private IOpenDocumentModel OpenDocumentModel;
private ICrossReferenceGuy CrossReferenceGuy;
private bool isOpen;
private IObservable<bool> openDocModelUpdateObservable;
private static TimeSpan period = TimeSpan.FromSeconds(20);
private IObservable<long> observable = Observable.Interval(period);
public readonly Subject<bool> OpenDocModelUpdateActionSubject = new Subject<bool>();
public ICrossReferenceControlViewModel ReferenceControlViewModel => referenceControlViewModel;
public bool IsOpen
{
get { return isOpen; }
set { isOpen = value; }
}
public CrossReferenceControl(IOpenDocumentModel openDocumentModel, ICrossReferenceControlViewModel referenceControlViewModel, ICrossReferenceGuy crossReferenceGuy)
{
InitializeComponent();
this.referenceControlViewModel = referenceControlViewModel;
OpenDocumentModel = openDocumentModel;
CrossReferenceGuy = crossReferenceGuy;
//CrossReferenceControlViewModel controlViewModel = new CrossReferenceControlViewModel((OpenDocumentModel)openDocumentModel);
DataContext = referenceControlViewModel;
observable.Subscribe(O => OpenDocumentModel.UpdateCaptionsSubject.OnNext(IsOpen));
}
}
The code that updates via button click event (this works fine):
private void ButtonRefresh_Click(object sender, RoutedEventArgs e)
{
OpenDocumentModel.UpdateCaptionsSubject.OnNext(IsOpen);
}
Note: The codes are cut down to provide only what I think is essential.
I have a bunch of labels that I set their value in the designer and later during runtime update them, but after using them, I want to set them back to their default value. My intent with this is to reduce the amount of large code to help make it easier to read.
random example like, setting in the designer of lbl_fruit Text = no fruits available currently then
*code*
lbl_fruits.Text = "banana";
*code*
lbl_fruits.ResetText(); // I want something like this
lbl_fruits.Text = "no fruits available currently"; // Instead of this
The .ResetText(); doesn't work for this as the label text gets cleaned instead of returning to "no fruits available currently"
My current solution is making a custom label control.
public class ExLabel : Label
{
private string defaultValue = "";
public string DefaultValue
{
get { return defaultValue; }
set { defaultValue = value; this.Invalidate(); }
}
protected override void OnControlAdded(ControlEventArgs e)
{
defaultValue = this.Text;
MessageBox.Show("This code is being run");
base.OnControlAdded(e);
}
public void ResetValue()
{
this.Text = defaultValue;
}
}
This code currently solves my problem if I use the custom propriety I made, but for me the ideal solution would be to have the design-time text value as the default value and not an extra propriety I made. OnControlAdded() does not get executed, OnPaint() runs again when lbl_fruits.Text = "banana"; happens.
So the question is: Which event I can override so the code gets executed as soon as the label is loaded but doesn't run twice. And also, is there a simpler way of approaching this?
In the end the solution I used was this:
public class ExLabel : Label
{
private string defaultValue = "";
public string DefaultValue
{
get { return defaultValue; }
set { defaultValue = value; this.Invalidate(); }
}
protected override void OnPaint(PaintEventArgs e)
{
if(defaultValue == "" && !this.Text.Contains("exLabel"))
{
defaultValue = this.Text;
}
base.OnPaint(e);
}
public void ResetValue()
{
this.Text = defaultValue;
}
}
public class ExLabel : Label
{
private string defaultValue = "";
public string DefaultValue
{
get { return defaultValue; }
set { defaultValue = value; this.Invalidate(); }
}
protected override void OnControlAdded(ControlEventArgs e)
{
defaultValue = this.Text;
MessageBox.Show("This code is being run");
base.OnControlAdded(e);
}
public void ResetValue()
{
this.Text = defaultValue;
}
}
I created a ListBoxItem where I have a property Name and override ToString() to give back name. That works nicely when I add new items.
But now I need to force the ListBox to update the labels when I change the name of my ship. I thought Refresh or Update would do that but that doesn't work.
I might be missing something very easy here.
public class ShipListBoxItem
{
public ListBox Parent { get; set; }
public ShipType Ship { get; set; }
public ShipListBoxItem()
{
Ship = new ShipType();
}
public ShipListBoxItem(ShipType st)
{
Ship = st;
}
public override string ToString()
{
return Ship.Name;
}
public void UpdateListBox()
{
Parent.Refresh(); //My problem is here. Update doesn't work either.
}
public static ShipListBoxItem AddToListBox(ListBox lb, ShipType ship)
{
ShipListBoxItem li = new ShipListBoxItem(ship);
li.Parent = lb;
lb.Items.Add(li);
return li;
}
}
If you use a List<T> as the DataSource for the listbox it is pretty easy to have changes to items show up. It also means there is no real reason to have a special class for adding a ShipListBoxItem to a ListBox, your basic Ship class may work:
class ShipItem
{
public enum ShipTypes { BattleShip, Carrier, Destroyer, Submarine, Frigate };
public ShipTypes Ship { get; set; }
public string Name { get; set; }
public ShipItem(string n, ShipTypes st)
{
Name = n;
Ship = st;
}
public override string ToString()
{
return String.Format("{0}: {1}", Ship.ToString(), Name);
}
}
The form related stuff:
private void Form1_Load(object sender, EventArgs e)
{
// add some ships
Ships = new List<ShipItem>();
Ships.Add(new ShipItem("USS Missouri", ShipTypes.BattleShip));
Ships.Add(new ShipItem("USS Ronald Reagan", ShipTypes.Carrier));
lb.DataSource = Ships;
}
private void button1_Click(object sender, EventArgs e)
{
// change a ship name
lb.DataSource = null; // suspend binding
this.Ships[0].Name = "USS Iowa";
lb.DataSource = Ships; // rebind
lb.Refresh();
}
As an alternative, you can also tell the Listbox to use a specific property for the display using DisplayMember:
lb.DataSource = Ships;
lb.DisplayMember = "Name";
This would use the Name property in the listbox instead of the ToString method. If your list is changing a lot, use a BindingList instead. It will allow changes to the list show up in the ListBox as you add them without toggling the DataSource.
Try this
ListBox.RefreshItems()
msdn
EDIT: You can use an extended class like this:
public class FooLisBox : System.Windows.Forms.ListBox
{
public void RefreshAllItems()
{
RefreshItems();
}
}
private void button1_Click(object sender, EventArgs e)
{
(listBox1.Items[0] as ShipListBoxItem).Ship.Name = "AAAA";
listBox1.RefreshAllItems();
}
I managed to solve my problem.
Mostly, thanks Jose M.
I ran into a problem however. RefreshItems() triggers OnSelectedIndexChanged()
so my overridden class looks like this
public class MyListBox : ListBox
{
public bool DoEvents{ get; set; } // Made it public so in the future I can block event triggering externally
public MyListBox()
{
DoEvents = true;
}
public void RefreshAllItems()
{
SuspendLayout();
DoEvents = false;
base.RefreshItems(); // this triggers OnSelectedIndexChanged as it selects the selected item again
DoEvents = true;
ResumeLayout();
}
// I only use this event but you can add all events you need to block
protected override void OnSelectedIndexChanged(EventArgs e)
{
if (DoEvents)
base.OnSelectedIndexChanged(e);
}
}
In a page, I have an event handler that sets 'Visible' to false on one control and true on another. Stepping through debug, I see that these values get set properly, and the control marked visible goes through OnPreRender while the control I have set to invisible does not. So all of that seems to be working as expected. However, when the request completes, the visibility has not changed at all on the page. I've tried setting the directly parent UpdatePanel to 'always' and have tried manually calling 'Update()' on it with no effect. Any clue as to what is going on here?
UPDATE:
I have found that it is only setting a private property on my user control that causes this whole thing to not work. I have included an example of that control and all of the places it references the private field.
Example:
protected override void OnLoad(EventArgs e)
{
if (this.IsPostback)
{
return;
}
this.Control1.Visible = true;
this.Control2.Visible = false;
}
protected void OnButtonClicked (object sender, EventArgs e)
{
this.Control1.Visible = false;
this.Control2.Visible = true;
// this has desired results when it fires
}
protected void OnUserControlEventThatFiresAfterRowCommand (object sender, EventArgs e)
{
this.Control2.SomeProp = this.GetSomeObject();
this.Control1.Visible = false;
this.Control2.Visible = true;
// this does not have desired results, even though it does fire
}
And then in Control2:
private SomeClass privatefield;
public SomeClass SomeProp
{
get
{
return this.privatefield;
}
set
{
this.PopulateFields(value);
this.privatefield = value;
// If I comment out this line it works!
}
}
protected override void LoadViewState(object savedState)
{
object[] state = savedState as object[];
base.LoadViewState(state[0]);
this.Enabled = state[1] as bool? ?? true;
this.SomeProp = state[2] as SomeClass;
this.Visible = state[3] as bool? ?? true;
}
protected override object SaveViewState()
{
return new object[]
{
base.SaveViewState(),
this.Enabled,
this.SomeProp,
this.Visible
};
}
I finally found out why by looking at the actual response body.
156|error|500|Error serializing value 'withheld class name' of type 'withheld class name'|
Why this error was not being thrown in debug is beyond me, but for anyone else reading this question looking for answers, look at your response bodies! It is because I was trying to put my instance of a class into Viewstate but that class was not marked with the Serializable attribute.
Ok so I am trying to pass a boolean from my Login form to my Home form, normally this would be fine for me and I would just use a property. However I thought I could use a similar method this time but I am implementing the singleton factory on the forms.
Here is the Login code relevant to this:
The AdminAccess property gets set fine and I have checked the value is correct.
private bool adminAccess;
public bool AdminAccess
{
get { return adminAccess; }
private set { adminAccess = value; }
}
private void btnLogin_Click(object sender, EventArgs e)
{
//Some Code Does Stuff
OpenHome();
}
private void OpenHome()
{
HomeForm CreateHomeForm = HomeForm.HomeUI;
CreateHomeForm.StartupHome = this;
//Trying to set the property.
CreateHomeForm.AdminPermissions= this.AdminAccess;
CreateHomeForm.Show();
this.Hide();
}
Here is the relevant code from the Home form:
public HomeForm()
{
InitializeComponent();
//just to check what is in the property quickly
textBox1.Text = AdminPermissions.ToString();
}
private bool adminPermissions;
public bool AdminPermissions
{
private get { return adminPermissions; }
set { adminPermissions = value; }
}
public Form StartupHome
{
set;
get;
}
private static HomeForm homeUI;
public static HomeForm HomeUI
{
get
{
if (homeUI == null || homeUI.IsDisposed)
{
homeUI = new HomeForm();
}
return homeUI;
}
}
The value gets reset when the HomeUI if loop runs as a new instance of the form is created. I can't seem to think how to modify this to get a working solution. As you can tell I am fairly amateur so I'm just looking for a quick and clean solution to this :)
Thank you very much for your time in advance!
You assign the value in the constructor, BEFORE the AdminPermissions property is actually set. Change your code like this
public class HomeForm
{
public HomeForm()
{
InitializeComponent();
}
private bool adminPermissions;
public bool AdminPermissions
{
get { return adminPermissions; }
set {
adminPermissions = value;
textBox1.Text = value.ToString();
}
}
...
}
Try setting the textBox1.Text value in one of the Form events. Try Loaded first, then Activated. You're resetting it to false every time in your constructor!