WPF/C#5.0 Publish/Subscribe to an array of static fields? - c#

Using C# 5.0, I'm creating a publish/subscribe relationship on a static field, so that I can access it from multiple pages. In the host window, I have
public enum PLCStates
{
Good,
Bad,
Disabled
};
public static class PLCSafeStates
{
public static event EventHandler testStates1Changed;
private static PLCStates _testStates1;
public static PLCStates testStates1
{
get { return _testStates1; }
set
{
if (value != _testStates1)
{
_testStates1 = value;
if (testStates1Changed != null)
testStates1Changed(null, EventArgs.Empty);
}
}
}
}
And then in the pages hosted by the window, I have things like:
public FB1()
{
InitializeComponent();
SafteyFaults.PLCSafeStates.testStates1Changed += PLCSafeStates_testStates1Changed;
}
private void PLCSafeStates_testStates1Changed(object sender, EventArgs e)
{
var test2 = SafteyFaults.PLCSafeStates.testStates1;
if (test2 == SafteyFaults.PLCStates.Bad)
{
VisualStateManager.GoToState(btnFB, "PLCBad", true);
}
if (test2 == SafteyFaults.PLCStates.Good)
{
VisualStateManager.GoToState(btnFB, "PLCGood", false);
}
}
private void btnFB_Click(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(btnOut1, "PLCBad",false);
if (SafteyFaults.PLCSafeStates.testStates1 == SafteyFaults.PLCStates.Good)
SafteyFaults.PLCSafeStates.testStates1=SafteyFaults.PLCStates.Bad;
else
SafteyFaults.PLCSafeStates.testStates1 = SafteyFaults.PLCStates.Good;
}
(right now, I don't have any business logic wired up yet- once I get this working, I'll link to actual data).
Anyhow, all of this works to create a single field I can subscribe to, modify, etc. But I need 20+ of these fields. I want to make 'testStates1' an array, but I've not been able to get it to work.
If I make the following edits to the code shown so far, it compiles and runs, but throws an error when I actually try to access the field (e.g. click on the button to change it):
//window
public static class PLCSafeStates
{
public static event EventHandler testStates1Changed;
private static PLCStates[] _testStates1;
public static PLCStates[] testStates1
{
get { return _testStates1; }
set
{
if (value != _testStates1)
{
_testStates1 = value;
if (testStates1Changed != null)
testStates1Changed(null, EventArgs.Empty);
}
}
}
}
//page
public FB1()
{
InitializeComponent();
SafteyFaults.PLCSafeStates.testStates1Changed += PLCSafeStates_testStates1Changed;
}
private void PLCSafeStates_testStates1Changed(object sender, EventArgs e)
{
var test2 = SafteyFaults.PLCSafeStates.testStates1[0];
if (test2 == SafteyFaults.PLCStates.Bad)
{
VisualStateManager.GoToState(btnFB, "PLCBad", true);
}
if (test2 == SafteyFaults.PLCStates.Good)
{
VisualStateManager.GoToState(btnFB, "PLCGood", false);
}
}
private void btnFB_Click(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(btnOut1, "PLCBad",false);
if (SafteyFaults.PLCSafeStates.testStates1[0] == SafteyFaults.PLCStates.Good)
SafteyFaults.PLCSafeStates.testStates1[0]=SafteyFaults.PLCStates.Bad;
else
SafteyFaults.PLCSafeStates.testStates1[0] = SafteyFaults.PLCStates.Good;
}

Related

Differentiate items loading to list vs scroll event Xamarin forms listview

This is my code that can successfully detect scroll up or down:
MyListView.ItemAppearing += async (object sender, ItemVisibilityEventArgs e) =>
{
var currentIdx = CurrentList.IndexOf((MyClass)e.Item);
if (currentIdx > _lastItemAppearedIdx)
ShowChopped();
else
ShowFull();
_lastItemAppearedIdx = CurrentList.IndexOf((MyClass)e.Item);
};
What is working is the following: Items get added to the list, then once i start scrolling it works fine where ShowChoppedand ShowFull are methods with animations that just makes a simple animation to either half the size of an object or make it full. This works fine, but if i however click a new category that changes the content in the list, ItemAppearing gets triggered of course and ShowChoppedand ShowFull are called even though i only want it called during a scrollevent.
How would i be able to differentiate a scroll to a item collection change? I have only tried this on iOS.
Updated code:
public class ListView_iOS : ListViewRenderer
{
private IDisposable _offsetObserver;
private double _prevYOffset;
private IListView _myListView;
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged(e);
if (e.NewElement is IListView)
_offsetObserver = Control.AddObserver("contentOffset",
Foundation.NSKeyValueObservingOptions.New, HandleAction);
}
private static bool CloseTo(double x, double y)
{
return Math.Abs(x - y) < 0.1;
}
private void HandleAction(Foundation.NSObservedChange obj)
{
var effectiveY = Math.Max(Control.ContentOffset.Y, 0);
if (!CloseTo(effectiveY, _prevYOffset) && Element is IListView)
{
var myList = Element as IListView;
myList.IsScrolling = true;
}
}
}
You can differentiate items loading from list scrolling by
1 adding the code if (EmployeeView.IsScrolling) within ItemAppearing method.
2 adding the code EmployeeView.IsScrolling = false; within any function you write to change the appearing of items without scrolling action, for example, when you add items or change category.
And the EmployeeView.IsScrolling value is set from listview renderer.
So the code is like:
NativeListView.cs
public class NativeListView : ListView
{
public static readonly BindableProperty
IsScrollingProperty =
BindableProperty.Create(nameof(IsScrolling),
typeof(bool), typeof(NativeListView), false);
public bool IsScrolling
{
get { return (bool)GetValue(IsScrollingProperty); }
set { SetValue(IsScrollingProperty, value); }
}
}
NativeAndroidListViewRenderer.cs
[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeAndroidListViewRenderer))]
namespace App2.Droid
{
public class NativeAndroidListViewRenderer : ListViewRenderer
{
public NativeAndroidListViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged(e);
if (e.NewElement is NativeListView)
Control.Scroll += Control_Scroll;
}
private void Control_Scroll(object sender, AbsListView.ScrollEventArgs e)
{
var myList = Element as NativeListView;
myList.IsScrolling = true;
}
}
}
NativeiOSListViewRenderer.cs
private IDisposable _offsetObserver;
private double _prevYOffset;
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged(e);
if (e.NewElement is NativeListView)
_offsetObserver = Control.AddObserver("contentOffset",
Foundation.NSKeyValueObservingOptions.New, HandleAction);
}
private void HandleAction(Foundation.NSObservedChange obj)
{
var effectiveY = Math.Max(Control.ContentOffset.Y, 0);
if (!CloseTo(effectiveY, _prevYOffset) && Element is NativeListView)
{
var myList = Element as NativeListView;
myList.IsScrolling = true;
_prevYOffset = effectiveY;
}
}
private static bool CloseTo(double x, double y)
{
return Math.Abs(x - y) < 0.1;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing && _offsetObserver != null)
{
_offsetObserver.Dispose();
_offsetObserver = null;
}
}
MainPage.xaml.cs
namespace App2
{
public partial class MainPage : ContentPage
{
ObservableCollection<String> employeeList = new ObservableCollection<String>();
int count = 0;
public MainPage()
{
InitializeComponent();
AddButtion.Clicked += AddButtion_Clicked;
DelButtion.Clicked += DelButtion_Clicked;
EmployeeView.ItemsSource = employeeList;
EmployeeView.ItemAppearing += async (object sender, ItemVisibilityEventArgs e) =>
{
if (EmployeeView.IsScrolling) {
await DisplayAlert("ItemAppearing", e.Item + " row is appearing", "OK");
Console.WriteLine("ItemAppearing!!!!!!!!!!");
}
};
}
private void AddButtion_Clicked(object sender, EventArgs e)
{
employeeList.Add("Mr. Mono"+ count++);
EmployeeView.IsScrolling = false;
}
private void DelButtion_Clicked(object sender, EventArgs e)
{
if (employeeList.Count > 0) {
employeeList.RemoveAt(0);
}
EmployeeView.IsScrolling = false;
}
}
}

binding not working in another class

textBox binding logContent in another class.
If i change value of variable logContent in MainWindow all working right. But if i change value of variable logContent in Test.cs (when i click on button) it is not working.
MainWindow.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
textBox.DataContext = Log.Instance;
Log.Instance.logContent += "aaa" + Environment.NewLine; //this working
}
private void button_Click(object sender, RoutedEventArgs e)
{
Test.Instance.Volaj();
}
}
Log.cs
class Log
{
public string logContent { get; set; }
private static Log instance;
public static Log Instance
{
get
{
if (instance == null)
{
instance = new Log();
}
return instance;
}
}
}
Test.cs
class Test
{
private static Test instance;
public static Test Instance
{
get
{
if (instance == null)
{
instance = new Test();
}
return instance;
}
}
public void Volaj()
{
Log.Instance.logContent += "bbb" + Environment.NewLine; //not working
}
}
XAML of textBox
<TextBox
x:Name="textBox"
HorizontalAlignment="Left"
Height="154"
Margin="10,155,0,0"
TextWrapping="Wrap"
Text="{Binding logContent}"
VerticalAlignment="Top"
Width="497"/>
in your code, the DataBinding is not working in any class.
You can see this with this simple test (you have to add the Loaded-Event in the MainWindow.xaml):
public MainWindow()
{
InitializeComponent();
textBox.DataContext = Log.Instance;
Debug.WriteLine($"Text [ctor before change]: {textBox.Text}");
// Output:
Log.Instance.logContent += "aaa" + Environment.NewLine; //this working
Debug.WriteLine($"Text [ctor after change]: {textBox.Text}");
// Output:
}
private void button_Click(object sender, RoutedEventArgs e)
{
Test.Instance.Volaj();
}
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
Debug.WriteLine($"Text [Loaded before change]: {textBox.Text}");
// Output: aaa
Log.Instance.logContent += "bbb" + Environment.NewLine;
Debug.WriteLine($"Text [Loaded after change]: {textBox.Text}");
// Output: aaa
}
The TextBox.Text Property does not change during the constructor. After the constructor is finished, the view reads the DataContext once and updates its Text property. That's why you see the text in your TextBox. When the Loaded-Event is fired, the TextBox already has the text-value. Other changes will not be displayed.
To make the binding work, you have to notify the view, that a property has changed. The INotifyPropertyChanged-Interface exists for this reason. So if you change your Log.cs to the following, it should work:
class Log : INotifyPropertyChanged
{
private string _logContent;
public string logContent
{
get { return _logContent; }
set { _logContent = value; OnPropertyChanged(); }
}
private static Log instance;
public static Log Instance
{
get
{
if (instance == null)
{
instance = new Log();
}
return instance;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Multiple windows with the same functionalities

So i was wondering how i would make some kind of superwindow/superclass for my windows. The windows look the following:
The first picture shows how every single one of the windows would potentially look when pressed on one of the buttons. So each of these windows would have the same basic functionality but with some different returning objects which can be seen in the following code.
public partial class FlatsteelWindow : Window {
private string str;
private IList test;
public FlatSteel _returnObject { get; set; }
public double amount { get; set; }
public FlatsteelWindow(object inputObject) {
InitializeComponent();
_returnObject = inputObject as FlatSteel;
FetchList();
}
private void FetchList() {
test = MaterialLogic.GetFlatsteelList() as List<FlatSteel>;
foreach (FlatSteel flatSteel in test) {
flatsteelListView.Items.Add(flatSteel.Name);
}
}
private void flatsteelListView_PreviewMouseLeftButtonUp(object sender, RoutedEventArgs e) {
var item = (sender as ListView).SelectedItem;
if (item != null) {
_returnObject = flatsteelListView.SelectedItems as FlatSteel;
str = item.ToString();
amountTextbox.IsEnabled = true;
FindObject(str);
}
}
private void FindObject(string s) {
foreach (FlatSteel flatSteel in test) {
if (s.Equals(flatSteel.Name)) {
_returnObject = flatSteel;
}
}
}
private void submitButton_Click(object sender, RoutedEventArgs e) {
if (!string.IsNullOrEmpty(amountTextbox.Text)) {
amount = amountTextbox.Text.customParseToDouble();
this.Close();
}
else {
MessageBox.Show("Indtast venligst en værdi.");
}
}
}
This is the code for the shown Flatsteel window.
Here is the code which creates the window:
private void flatsteelButton_Click(object sender, RoutedEventArgs e) {
FlatsteelWindow flatsteelWindow = new FlatsteelWindow(this);
flatsteelWindow.ShowDialog();
test = flatsteelWindow._returnObject;
amount = flatsteelWindow.amount;
if (test != null && amount != null) {
AddToGridView();
}
}
public FlatSteel test { get; set; }
private double amount { get; set; }
As we can see in this code the flatsteel object is needed where this method is invoked. The 6 different windows, which each are represented by the six buttons on the 2nd picture, each return an object to their type of material. So to make it clear what my question is:
How do i make one superclass/superwindow that has all the same basic funtionality, from which i then can inherit from and add the needed functionality for the different returning objects?

Global Variables Return Unexpected Values

Well I was trying to assign global variable to read values from Check boxes and radio buttons but the values don't update when the selections are changed ! Where have I done wrong? Here's the code:
private void chkInMut_Checked(object sender, RoutedEventArgs e)
{
GlobalVar.Mutate = 1;
}
private void chkShwCal_Checked(object sender, RoutedEventArgs e)
{
GlobalVar.ShowCal = 1;
}
private void chkOutSol_Checked(object sender, RoutedEventArgs e)
{
GlobalVar.OutCal = 1;
}
}
public static class GlobalVar
{
static int _MaxMin, _MutVal, _CalShow, _CalOut;
/// <summary>
/// Access routine for global variable.
/// </summary>
public static int Extrema
{
get
{
return _MaxMin;
}
set
{
_MaxMin = value;
}
}
public static int Mutate
{
get
{
return _MutVal;
}
set
{
_MutVal = value;
}
}
public static int ShowCal
{
get
{
return _CalShow;
}
set
{
_CalShow = value;
}
}
public static int OutCal
{
get
{
return _CalOut;
}
set
{
_CalOut = value;
}
}
}
when I try to print the numbers using this test satement, the values returned are unexpected :
maxMin = GlobalVar.Extrema;
calShow = GlobalVar.ShowCal;
calOut = GlobalVar.OutCal;
IsMutble = GlobalVar.Mutate;
txtOutput.Text += Convert.ToString("\nMaxima Minima"+maxMin+"\n"+"Show Cal : "+calShow+"\n"+"Output Cal :"+calOut+"\n"+"Mutate : "+IsMutble+"\n---------\n");
And when I check/un-check the boxes, the values are not updated as it should be. Where have I gone wrong?
Edit: Solved by adding Unchecked Parameter.
Probably you should write your event handlers like this
private void chkInMut_Checked(object sender, RoutedEventArgs e)
{
GlobalVar.Mutate = (chkInMut.IsChecked ? 1 : 0);
}
and so on .....
I think the problem is with your public static properties. for example try this:
public static int Extrema
{
get
{
return GlobalVar._MaxMin;
}
set
{
GlobalVar._MaxMin = value;
}
}
and do the same for all other properties.
Edit:
and why are you using this stucture? You can set your static class to be like this:
public static class GlobalVar
{
public static int Extrema;
public static int Mutate;
public static int ShowCal;
}

How to add validation to PropertyGrid's CollectionEditor?

I'm using PropertyGrid to edit an object containing a collection.
Collection is edited using the CollectionEditor.
I have to make sure elements in collection are unique.
How can I add validation to CollectionEditor:
By either overloading CollectionEditor's OnFormClosing
Or adding validation for creating/editing items?
You can create your own collection editor, and hook into events on the default editor's controls. You can use these events to, say, disable the OK button. Something like:
public class MyCollectionEditor : CollectionEditor
{
private static Dictionary<CollectionForm, Button> okayButtons
= new Dictionary<CollectionForm, Button>();
// Inherit the default constructor from CollectionEditor
public MyCollectionEditor(Type type)
: base(type)
{
}
// Override this method in order to access the containing user controls
// from the default Collection Editor form or to add new ones...
protected override CollectionForm CreateCollectionForm()
{
CollectionForm collectionForm = base.CreateCollectionForm();
collectionForm.FormClosed +=
new FormClosedEventHandler(collectionForm_FormClosed);
collectionForm.Load += new EventHandler(collectionForm_Load);
if (collectionForm.Controls.Count > 0)
{
TableLayoutPanel mainPanel = collectionForm.Controls[0]
as TableLayoutPanel;
if ((mainPanel != null) && (mainPanel.Controls.Count > 7))
{
// Get a reference to the inner PropertyGrid and hook
// an event handler to it.
PropertyGrid propertyGrid = mainPanel.Controls[5]
as PropertyGrid;
if (propertyGrid != null)
{
propertyGrid.PropertyValueChanged +=
new PropertyValueChangedEventHandler(
propertyGrid_PropertyValueChanged);
}
// Also hook to the Add/Remove
TableLayoutPanel buttonPanel = mainPanel.Controls[1]
as TableLayoutPanel;
if ((buttonPanel != null) && (buttonPanel.Controls.Count > 1))
{
Button addButton = buttonPanel.Controls[0] as Button;
if (addButton != null)
{
addButton.Click += new EventHandler(addButton_Click);
}
Button removeButton = buttonPanel.Controls[1] as Button;
if (removeButton != null)
{
removeButton.Click +=
new EventHandler(removeButton_Click);
}
}
// Find the OK button, and hold onto it.
buttonPanel = mainPanel.Controls[6] as TableLayoutPanel;
if ((buttonPanel != null) && (buttonPanel.Controls.Count > 1))
{
Button okayButton = buttonPanel.Controls[0] as Button;
if (okayButton != null)
{
okayButtons[collectionForm] = okayButton;
}
}
}
}
return collectionForm;
}
private static void collectionForm_FormClosed(object sender,
FormClosedEventArgs e)
{
CollectionForm collectionForm = (CollectionForm)sender;
if (okayButtons.ContainsKey(collectionForm))
{
okayButtons.Remove(collectionForm);
}
}
private static void collectionForm_Load(object sender, EventArgs e)
{
ValidateEditValue((CollectionForm)sender);
}
private static void propertyGrid_PropertyValueChanged(object sender,
PropertyValueChangedEventArgs e)
{
ValidateEditValue((CollectionForm)sender);
}
private static void addButton_Click(object sender, EventArgs e)
{
Button addButton = (Button)sender;
ValidateEditValue((CollectionForm)addButton.Parent.Parent.Parent);
}
private static void removeButton_Click(object sender, EventArgs e)
{
Button removeButton = (Button)sender;
ValidateEditValue((CollectionForm)removeButton.Parent.Parent.Parent);
}
private static void ValidateEditValue(CollectionForm collectionForm)
{
if (okayButtons.ContainsKey(collectionForm))
{
Button okayButton = okayButtons[collectionForm];
IList<MyClass> items = collectionForm.EditValue as IList<MyClass>;
okayButton.Enabled = MyCollectionIsValid(items);
}
}
private static bool MyCollectionIsValid(IList<MyClass> items)
{
// Perform validation here.
return (items.Count == 2);
}
}
You will also need to add an Editor attribute to you collection:
class MyClass
{
[Editor(typeof(MyCollectionEditor),
typeof(System.Drawing.Design.UITypeEditor))]
List<Foo> MyCollection
{
get; set;
}
}
NOTE: I found that the value of items in removeButton_Click was not correct - so some tweaking may need to take place.
Try collectionForm.Context.Instance and typecast it to your data type this should do the trick.

Categories

Resources