Refresh WPF label - c#

I'm trying to dynamically refresh the label that shows the current amount of space remaining but unfortunately the number doesn't refresh. Do you have any idea how to solve my problem?
C#
private void ReqDescText_Changed(object sender, TextChangedEventArgs e)
{
Counter ReqDescText_Counter = new Counter(ReqDescText, ReqDescLabelLength);
}
Class
public class Counter
{
public TextBox InputTextbox { get; set; }
public Label CounterLabel { get; set; }
public Counter(TextBox InputTextbox, Label CounterLabel)
{
int NB;
TextBox textBox = new TextBox();
var tempText = textBox.Text;
NB = (InputTextbox.MaxLength - tempText.Length);
CounterNumber counterNumber = new CounterNumber { Number = NB.ToString() };
CounterLabel.Content = counterNumber;
if (NB == 0)
{
CounterLabel.Foreground = new SolidColorBrush(Colors.Red);
}
}
class CounterNumber
{
public string Number { get; set; }
public override string ToString()
{
return "[" + Number + "]";
}
}
}
WPF
<Label x:Name ="ReqDescLabel" Content="Description" Grid.Row="1" Margin="5,5,0,5" Grid.Column="0"/>
<Label Name="ReqDescLabelLength" FontSize="10" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,6"/>
<TextBox x:Name ="ReqDescText" Padding="3" Grid.Row="1" Margin="0,5,0,5" Grid.Column="2" TextWrapping="Wrap" SpellCheck.IsEnabled="True" MaxLength="250" TextChanged="ReqDescText_Changed" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True"/>
How it's looks like

Problem solved. My mistake.
I did't take the current length of the text.
public class Counter
{
public TextBox InputTextbox { get; set; }
public Label CounterLabel { get; set; }
public Counter(TextBox InputTextbox, Label CounterLabel)
{
int NB;
var tempText = InputTextbox.Text;
NB = (InputTextbox.MaxLength - tempText.Length);
CounterNumber counterNumber = new CounterNumber { Number = NB.ToString() };
CounterLabel.Content = counterNumber;
if (NB == 0)
{
CounterLabel.Foreground = new SolidColorBrush(Colors.Red);
}
}
class CounterNumber
{
public string Number { get; set; }
public override string ToString()
{
return "[" + Number + "]";
}
}
}

Related

UWP C# How to append .json file and to identify them in groups

I am trying to create a usercontrol to display groups of items from json file on windows iot core.
I have a "Create Group" button. Once pressed, it will create 64 usercontrols with respective details and display in a scrollviewer. susequently i can edit individual items in any of the 64 usercontrol then save the json file.
I have my class for the usercontrol as follow;
I have having an issue on how to create different groups of the 64 items and append all in a same json file and to subsequently display them from selection of the mentioned different groups.
Please help thanks.
Group Class
[DataContract]
public class DecoderGroup
{
[DataMember]
public int groupID{ get; set; }
[DataMember]
public string groupName{ get; set; }
[DataMember]
public int cardAddress { get; set; }
[DataMember]
public bool enabled { get; set; }
[DataMember]
public int z1label { get; set; }
[DataMember]
public int z2label { get; set; }
[DataMember]
public int z3label { get; set; }
[DataMember]
public int z4label { get; set; }
[DataMember]
public bool zone1 { get; set; }
[DataMember]
public bool zone2 { get; set; }
[DataMember]
public bool zone3 { get; set; }
[DataMember]
public bool zone4 { get; set; }
[DataMember]
public List<byte> txData { get; set; }
public DecoderGroup(int id, int address, int z1, int z2, int z3, int z4)
{
groupName = "Group";
zone1 = false;
zone2 = false;
zone3 = false;
zone4 = false;
z1label = z1;
z2label = z2;
z3label = z3;
z4label = z4;
}
}
MainPage.xaml.cs
private void AddGroup_Click(object sender, RoutedEventArgs e)
{
ZonesList_Panel.Children.Clear();
int groupid = 1;
int cardadr;
for (cardadr = 1; cardadr <= MAXCARDS; cardadr++)
{
var z4 = (4 * cardadr);
var z3 = (4 * cardadr) - 1;
var z2 = (4 * cardadr) - 2;
var z1 = (4 * cardadr) - 3;
DecoderGroupUserControl decoderGroupControl = new DecoderGroupUserControl(this, new DecoderGroup(groupid, cardadr, z1, z2, z3, z4));
ZonesList_Panel.Children.Add(decoderGroupControl);
}
}
private async void SaveGroup_Click(object sender, RoutedEventArgs e)
{
await saveGroupsToJSON(getGroups());
}
public async Task saveGroupsToJSON(List<DecoderGroup> groups)
{
var serializer = new DataContractJsonSerializer(typeof(List<DecoderGroup>));
using (var stream = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync(DECODERGROUPS_FILE, CreationCollisionOption.OpenIfExists))
{
serializer.WriteObject(stream, groups);
}
}
public List<DecoderGroup> getGroups()
{
List<DecoderGroup> ret = new List<DecoderGroup>();
foreach (DecoderGroupUserControl u in ZonesList_Panel.Children)
{
//add condition for group ID
ret.Add(u.decoderGroup);
}
return ret;
}
UserControl
public DecoderGroupUserControl(MainPage page, DecoderGroup group)
{
this.InitializeComponent();
mainPage = page;
this.decoderGroup = group;
Z1Name.Text = group.z1label.ToString();
Z2Name.Text = group.z2label.ToString();
Z3Name.Text = group.z3label.ToString();
Z4Name.Text = group.z4label.ToString();
}
It is recommended to use UserControl as the DateTemplate of ListView so that you don’t need to create multiple usercontrol and add them to the page. Then you could read json file and convert json objects to a collection, and you could use the collection as the Itemsource of ListView.
By implementing the INotifyPropertyChanged interface, TwoWay data binding could reflect the UI changes to the collection. Finally, you could write the changed collection to the json file.
Note that you need to download the Newtonsoft.Json to parse the json object via Manage NuGet Packages. Please refer to the following code.
MyUserControl1.xaml:
<UserControl
..>
<Grid>
<!--customize the usercontrol style-->
<StackPanel>
<StackPanel Orientation="Horizontal" >
<TextBox Margin="0,0,20,0" Text="{Binding Name,Mode=TwoWay}" BorderThickness="0"/>
<TextBox Text="{Binding Job,Mode=TwoWay}" BorderThickness="0"/>
</StackPanel>
<TextBox Text="{Binding Age,Mode=TwoWay}" BorderThickness="0"/>
</StackPanel>
</Grid>
</UserControl>
MainPage.xaml:
<Page..>
<Grid>
<StackPanel>
<ListView ItemsSource="{x:Bind Results,Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Person">
<local:MyUserControl1>
</local:MyUserControl1>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button x:Name="SaveButton" Content="Save" Click="SaveButton_Click"/>
</StackPanel>
</Grid>
</Page>
MainPage.xaml.cs:
namespace WriteJson
{
public sealed partial class MainPage : Page
{
public ObservableCollection<Person> Persons { get; set; }
public ObservableCollection<Person> Results { get; set; }
public string path;
public MainPage()
{
this.InitializeComponent();
CreateJsonFile();
path = ApplicationData.Current.LocalFolder.Path + "\\info.json";
Results = JsonConvert.DeserializeObject<ObservableCollection<Person>>(File.ReadAllText(path));
Debug.WriteLine("bind successfully");
}
public async void CreateJsonFile()
{
//check if info.json exists, if it doesn't exist, create it
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file;
try
{
file = await folder.GetFileAsync("info.json");
}
catch
{
await folder.CreateFileAsync("info.json");
Persons = new ObservableCollection<Person>()
{
new Person(){Name="tom",Job="teacher",Age=24},
new Person(){Name="lily",Job="nurse",Age=20},
new Person(){Name="ming",Job="student",Age=26},
new Person(){Name="kiki",Job="lawyer",Age=28},
new Person(){Name="jack",Job="worker",Age=21},
};
path = ApplicationData.Current.LocalFolder.Path + "\\info.json";
File.WriteAllText(path, JsonConvert.SerializeObject(Persons));
Debug.WriteLine("create a json file successfully");
}
}
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
File.WriteAllText(path, JsonConvert.SerializeObject(Results));
Debug.WriteLine("save successfully");
}
}
public class Person:INotifyPropertyChanged
{
private string name;
private string job;
private int age;
public string Name
{
get { return name; }
set
{
name = value;
RaisePropertyChanged("Name");
}
}
public string Job
{
get { return job; }
set
{
job = value;
RaisePropertyChanged("Job");
}
}
public int Age
{
get { return age; }
set
{
age = value;
RaisePropertyChanged("Age");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyname=null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
}

Object property change in bound ObservableCollection doesn't change UI values

I'm trying to create a UWP-app in C# that can control my lights in my home. I am able to fetch the data from the server and create lamp objects for each individual lamp. These lamp objects are then place in an ObservableCollection on the beginning of the app. This ObservableCollection is bound to a GridView with an DataTemplate. When the app started i can see my lights with the right data. I then refetch the data to check if any lamp property has changed every 500ms. I can clearly see that the object properties are succesfully updated, but the bound data doesn't recognize this change. So the UI does not change either. I tried to use the NotifyPropertyChange in Lamp class, but that did nothing either.
After a lot of trial and error i found that the ui only changes when I add, delete or replace an object in the ObservableCollection, but replacing is not really a practical option for me as it causes a lot of instabillity and does not look like that is the way this problem has to be resolved.
<GridView ItemsSource="{x:Bind LampCollection}" Margin="10 0" HorizontalAlignment="Center">
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:Lamp">
<Border BorderBrush="#555555" BorderThickness="1" CornerRadius="8" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10" >
<Grid Width="300" Height="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Grid.Column="0" Source="{x:Bind ImageUri, Mode=OneWay}" Width="80" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" >
<TextBlock Name="txt" VerticalAlignment="Bottom" FontSize="20" FontWeight="Bold" Margin="10,0,0,20" Text="{x:Bind Name, Mode=OneTime}"/>
<TextBlock Name="status" VerticalAlignment="Bottom" FontSize="11" FontWeight="Bold" Margin="10,0,0,20" Text="{x:Bind Status, Mode=OneWay}"/>
</StackPanel>
<Rectangle Grid.Row="1" Grid.Column="0" Visibility="{x:Bind ColorLamp}" Width="50" Height="50" Fill="Maroon"/>
<Slider Visibility="{x:Bind Dimmable}" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="10,0,10,0" Value="{x:Bind Brightness, Mode=TwoWay}"/>
</Grid>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Xaml code
The lamp.SetStatus function just parses the string and sets the properties Brightness and Status which are bound to the UI.
foreach (Lamp lamp in LampCollection) {
string response = await GetAsync(UrlString + lamp.IDX.ToString());
dynamic json = JsonConvert.DeserializeObject(response);
if (json.status == "OK") {
lamp.SetStatus(json.result[0].Status.ToString());
}
}
C# update code
Edit
I tried to implement the INotifyPropertyChanged in my lamp class as described in Microsoft's documentation. It doesn't seem to do anything however. I also tried passing in the name in the NotifyPropertyChanged() function but that only made my app crash.
class Lamp : INotifyPropertyChanged {
public uint IDX { get; internal set; }
public string Name { get; internal set; }
public bool Status { get; internal set; }
public string ImageUri { get; internal set; }
public bool Dimmable { get; internal set; }
public bool ColorLamp { get; internal set; }
public uint Brightness { get; set; }
public float[] Color { get; set; }
public Lamp(uint idx, string name, string status, bool dimmable, bool colorLamp) {
IDX = idx;
Name = name;
Color = new float[3];
Dimmable = dimmable;
ColorLamp = colorLamp;
if (status == "Off") {
ImageUri = "Images/lamp-off.svg";
Status = false;
} else {
ImageUri = "Images/lamp-on.svg";
Status = true;
if(dimmable) {
Brightness = uint.Parse(Regex.Match(status, #"\d+").Value, NumberFormatInfo.InvariantInfo);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void Switch(bool status) {
Status = status;
if(status) ImageUri = "Images/lamp-on.svg";
else ImageUri = "Images/lamp-off.svg";
NotifyPropertyChanged();
}
public void SetColor(float r, float g, float b) { if (ColorLamp) { Color[0] = r; Color[1] = g; Color[2] = b; } }
public void SetStatus(string status) {
if (status == "Off") {
if (Status) {
ImageUri = "Images/lamp-off.svg";
Status = false;
if (Dimmable) Brightness = 0;
Debug.WriteLine(Name + "(" + IDX + ") has turned off");
NotifyPropertyChanged();
}
} else {
if (Dimmable) {
uint _tmpBright = uint.Parse(Regex.Match(status, #"\d+").Value, NumberFormatInfo.InvariantInfo);
if(!Status || Brightness != _tmpBright) {
ImageUri = "Images/lamp-on.svg";
Status = true;
Brightness = _tmpBright;
Debug.WriteLine(Name + "(" + IDX + ") has turned on or changed brighntess");
NotifyPropertyChanged();
}
} else {
if (!Status) {
ImageUri = "Images/lamp-on.svg";
Status = true;
Debug.WriteLine(Name + "(" + IDX + ") has turned on");
NotifyPropertyChanged();
}
}
}
}
}
Based your code snippet, you called the NotifyPropertyChanged() method in the SetStatus() method and the CallerMemberName allows you to obtain the method or property name of the caller to the method, if you do not pass any propertyName to the NotifyPropertyChanged() method, it will automatically obtain the method name which is SetStatus. However, there is no UI bound with SetStatus, so the UI won't update. If you want to update the UI which bound with Status and Brightness properties in this scenario, you could pass these two property names to NotifyPropertyChanged() method, for example:
public void SetStatus(string status)
{
if (status == "Off")
{
if (Status)
{
ImageUri = "Assets/2.jpg";
Status = false;
if (Dimmable) Brightness = 0;
Debug.WriteLine(Name + "(" + IDX + ") has turned off");
NotifyPropertyChanged("Status");
NotifyPropertyChanged("Brightness");
}
}
......
}
However, every time when you change the values of Status and Brightness properties in SetStatus() method or other methods in your Lamp class, you need to call NotifyPropertyChanged("xxx") method, it is a little complicated. You can declare a private variable and override get and set methods, in the set method, calling the NotifyPropertyChanged() method, every time set a new value to your property, it will enter the set method and then notify the UI to update. Take Status and Brightness as examples:
public class Lamp : INotifyPropertyChanged
{
private bool status { get; set; }
private uint brightness { get; set; }
public bool Status {
get {
return status;
}
set {
status = value;
NotifyPropertyChanged();
}
}
public uint Brightness
{
get
{
return brightness;
}
set
{
brightness = value;
NotifyPropertyChanged();
}
}
// The same behavior to the following properties
public uint IDX { get; internal set; }
public string Name { get; internal set; }
public string ImageUri { get; internal set; }
public bool Dimmable { get; internal set; }
public bool ColorLamp { get; internal set; }
public float[] Color { get; set; }
public Lamp(uint idx, string name, string status, bool dimmable, bool colorLamp)
{
IDX = idx;
Name = name;
Color = new float[3];
Dimmable = dimmable;
ColorLamp = colorLamp;
if (status == "Off")
{
ImageUri = "Assets/2.jpg";
Status = false;
}
else
{
ImageUri = "Assets/3.jpg";
Status = true;
if (dimmable)
{
Brightness = uint.Parse(Regex.Match(status, #"\d+").Value, NumberFormatInfo.InvariantInfo);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void Switch(bool status)
{
Status = status;
if (status) ImageUri = "Assets/3.jpg";
else ImageUri = "Assets/2.jpg";
}
public void SetColor(float r, float g, float b) { if (ColorLamp) { Color[0] = r; Color[1] = g; Color[2] = b; } }
public void SetStatus(string status)
{
if (status == "Off")
{
if (Status)
{
ImageUri = "Assets/2.jpg";
Status = false;
if (Dimmable) Brightness = 0;
}
}
else
{
if (Dimmable)
{
uint _tmpBright = 30;
if (!Status || Brightness != _tmpBright)
{
ImageUri = "Assets/3.jpg";
Status = true;
Brightness = _tmpBright;
}
}
else
{
if (!Status)
{
ImageUri = "Assets/3.jpg";
Status = true;
}
}
}
}
}

Binding a TreeView to a collection with different types of objects on same level

I want to bind my Treeview control to a collection that holds objects of type MeasurementResult. The MeasurementResult object itself has two collections, one for MeasurementInfo and one for DeviceInfo types.
After googling and searching on SO I found out that the best solution might be a CompositeCollection. The problem I have with that is that I just can't figure out how to define the (Hierarchical?!)DataTemplate's in a way that my data get shown in the Treeview in the way I want it.
Ultimately I would like to have a TreeView structure like that:
-MeasurementResult1
---MeasurementInformation
------MeasurementInformation1
------MeasurementInformation2
------MeasurementInformation3
---DeviceInformation
------DeviceInformation1
------DeviceInformation2
------DeviceInformation3
-MeasurementResult2
---MeasurementInformation
------MeasurementInformation1
------MeasurementInformation2
------MeasurementInformation3
---DeviceInformation
------DeviceInformation1
------DeviceInformation2
------DeviceInformation3
-MeasurementResultN
But the problem is that my current Treeview looks like that:
The nested properties for MeasurementData and DeviceData are not shown in my TreeView.
The code that I have so far, XAML:
<local:TreeViewSampleData x:Key="TreeViewSampleData"/>
<DataTemplate x:Key="MeasurementDataTemplate">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Finished: " Margin="0,0,10,0"/>
<TextBlock Text="{Binding Finished}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Median: " Margin="0,0,10,0"/>
<TextBlock Text="{Binding Median}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Maximum: " Margin="0,0,10,0"/>
<TextBlock Text="{Binding Maximum}" />
</StackPanel>
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="DeviceDataTemplate" DataType="{x:Type local:DeviceData}" ItemTemplate="{StaticResource MeasurementDataTemplate}"
ItemsSource="{Binding MeasurementData}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="MeasurementResultTemplate" DataType="{x:Type local:MeasurementResult}" ItemTemplate="{StaticResource DeviceDataTemplate}"
ItemsSource="{Binding Measurements}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<telerik:RadTreeView x:Name="tvMeasResults"
ItemsSource="{Binding Source={StaticResource TreeViewSampleData}, Path = MeasurementResults}"
ItemTemplate="{StaticResource MeasurementResultTemplate}"
>
</telerik:RadTreeView>
My related classes:
public class MeasurementResult
{
public string Name { get; set; } = "Measurement Result";
internal ObservableCollection<MeasurementInfo> MeasurementInfo { get; set; }
internal ObservableCollection<DeviceInfo> DeviceInfo { get; set; }
public CompositeCollection Measurements
{
get
{
var items = new CompositeCollection();
items.Add(new CollectionContainer { Collection = MeasurementInfo });
items.Add(new CollectionContainer { Collection = DeviceInfo });
return items;
}
}
public MeasurementResult()
{
MeasurementInfo = new ObservableCollection<MeasurementInfo>();
DeviceInfo = new ObservableCollection<DeviceInfo>();
}
}
public class MeasurementInfo
{
public string Name { get; set; } = "Measurement Information";
public ObservableCollection<MeasurementData> ThicknessData { get; set; }
public MeasurementInfo()
{
ThicknessData = new ObservableCollection<MeasurementData>();
}
}
public class MeasurementData
{
public DateTime Finished { internal set; get; }
public double Median { internal set; get; }
public double Maximum { internal set; get; }
public MeasurementData()
{
Finished = DateTime.Now;
Median = 150;
Maximum = 200;
}
}
public class DeviceInfo
{
public string Name { get; set; } = "Device Information";
public ObservableCollection<DeviceData> DeviceData { get; set; }
public DeviceInfo()
{
DeviceData = new ObservableCollection<DeviceData>();
}
}
public class DeviceData
{
public DateTime Finished { internal set; get; }
public int Port { internal set; get; }
public int Slot { internal set; get; }
public DeviceData()
{
Finished = DateTime.Now;
Port = 1;
Slot = 1;
}
}
What is wrong with my bindings? I guess the DataTemplates are wrong but I couldn't figure out how to define them to get my expected result.
This will allow you to add specific items to specific leaves and they will be concatenated by GetEnumerator so the TreeView presents things in the way you expected.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace WpfApp1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var item = new InformationTreeItem("ROOT")
{
Children =
{
new InformationTreeItem("Level 1")
{
DeviceInformation =
{
new DeviceInformation("Device 1/1"),
new DeviceInformation("Device 1/2")
},
MeasurementInformation =
{
new MeasurementInformation("Measure 1/1"),
new MeasurementInformation("Measure 1/2")
},
Children =
{
new InformationTreeItem("Level 2")
{
DeviceInformation =
{
new DeviceInformation("Device 2/1"),
new DeviceInformation("Device 2/2")
},
MeasurementInformation =
{
new MeasurementInformation("Measure 2/1"),
new MeasurementInformation("Measure 2/2")
},
Children =
{
new InformationTreeItem("Level 3")
}
}
}
}
}
};
DataContext = item;
}
}
public interface IInformation
{
string Description { get; }
}
public class InformationTreeItem : IEnumerable<IInformation>, IInformation
{
public InformationTreeItem(string description)
{
Description = description;
}
private InformationTreeItem(string description, IList<IInformation> children)
{
Description = description;
Children = children;
}
public IList<IInformation> Children { get; } = new List<IInformation>();
public IList<DeviceInformation> DeviceInformation { get; } = new List<DeviceInformation>();
public IList<MeasurementInformation> MeasurementInformation { get; } = new List<MeasurementInformation>();
public string Description { get; }
public IEnumerator<IInformation> GetEnumerator()
{
var list = new List<IInformation>();
if (DeviceInformation.Any())
{
list.Add(new InformationTreeItem(nameof(DeviceInformation), new List<IInformation>(DeviceInformation)));
}
if (MeasurementInformation.Any())
{
list.Add(new InformationTreeItem(nameof(MeasurementInformation), new List<IInformation>(MeasurementInformation)));
}
foreach (var child in Children)
{
list.Add(child);
}
return list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public override string ToString()
{
return Description;
}
}
public class DeviceInformation : IInformation
{
public DeviceInformation(string description)
{
Description = description;
}
public string Description { get; }
public override string ToString()
{
return Description;
}
}
public class MeasurementInformation : IInformation
{
public MeasurementInformation(string description)
{
Description = description;
}
public string Description { get; }
public override string ToString()
{
return Description;
}
}
}

Storing Radiobutton selection from different groups to a list

I need to get all selected radio button tags and group name after user clicks on Appbar button submit and store it in a List.
So i can compare the user submitted answer list with the list from the server..
If i use Checked="Answer_Checked" , List is over written when i click another radio button in Question 2
public class RootObject
{
public RootObject(int id, string question, int qno, int qcount)
{
this.id = id;
this.question = question;
this.qcount = qcount;
this.qno = qno;
}
public int id { get; set; }
public string question { get; set; }
public int qcount { get; set; }
public int qno { get; set; }
public int time { get; set; }
}
public class AnswerObject
{
public AnswerObject(int question_id, int answer_id, string answer, int is_right_option)
{
this.question_id = question_id;
this.answer_id = answer_id;
this.answer = answer;
this.is_right_option = is_right_option;
}
public int question_id { get; set; }
public int answer_id { get; set; }
public string answer { get; set; }
public int is_right_option { get; set; }
}
public class Question
{
public string QuestionName { get; set; }
public int qcount { get; set; }
public int qno { get; set; }
public ObservableCollection<Option> options { get; set; }
}
public class Option
{
public string QuestionAnswer { get; set; }
public string groupname { get; set; }
public int IsCorrect { get; set; }
}
C# Coding
var result1 = await response1.Content.ReadAsStringAsync();
var objResponse1 = JsonConvert.DeserializeObject<List<RootObject>>(result1);
var result2 = await response2.Content.ReadAsStringAsync();
var objResponse2 = JsonConvert.DeserializeObject<List<AnswerObject>>(result2);
for (int i = 0; i < objResponse1.LongCount(); i++)
{
ObservableCollection<Option> options1 = new ObservableCollection<Option>();
for (int j = 0; j < objResponse2.LongCount(); j++)
{
if (objResponse1[i].id == objResponse2[j].question_id)
{
options1.Add(new Option() { QuestionAnswer = objResponse2[j].answer, IsCorrect = objResponse2[j].is_right_option, groupname = objResponse2[j].question_id.ToString() });
}
}
questions.Add(new Question() { QuestionName = objResponse1[i].question, qno=i + 1, qcount =objResponse1.Count, options = options1 });
}
flipView.ItemsSource = questions;
XAML Coding
<FlipView x:Name="flipView" HorizontalAlignment="Left" VerticalAlignment="Top" ItemsSource="{Binding}" Margin="0,35,0,0">
<FlipView.ItemTemplate>
<DataTemplate>
<ListView Name="ItemData" SelectionMode="None" ItemsSource="{Binding}" >
<Grid x:Name="ContentPanel">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock x:Name="Testing" Margin="10,20" FontSize="22.333">
<Run Text="{Binding qno}"/>
<Run Text="of"/>
<Run Text="{Binding qcount}"/>
</TextBlock>
<TextBlock x:Name="Question" Text="{Binding QuestionName}" Margin="10,60" VerticalAlignment="Top" HorizontalAlignment="Left" FontSize="22.333" TextWrapping="Wrap"/>
<ListBox Grid.Row="1" Padding="0" Margin="10,-10" ItemsSource="{Binding options}" Background="Transparent">
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton x:Name="Answer" GroupName="{Binding groupname}" Checked="Answer_Checked" Tag="{Binding IsCorrect}" Margin="10,2">
<RadioButton.Content>
<TextBlock Text="{Binding QuestionAnswer}" Foreground="White"/>
</RadioButton.Content>
</RadioButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</ListView>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
Answer in JSON Format
[{"question_id":3,"answer_id":1,"answer":"10%","is_right_option":0},{"question_id":3,"answer_id":2,"answer":"10.25%","is_right_option":1},{"question_id":3,"answer_id":3,"answer":"10.5%","is_right_option":0},{"question_id":3,"answer_id":4,"answer":"None of these","is_right_option":0},{"question_id":4,"answer_id":5,"answer":"Rs. 2.04","is_right_option":1},{"question_id":4,"answer_id":6,"answer":"Rs. 3.06","is_right_option":0},{"question_id":4,"answer_id":7,"answer":"Rs. 4.80","is_right_option":0},{"question_id":4,"answer_id":8,"answer":"Rs. 8.30","is_right_option":0}]
Question in JSON Format
[{"id":3,"question":"An automobile financier claims to be lending money at simple interest, but he includes the interest every six months for calculating the principal. If he is charging an interest of 10%, the effective rate of interest becomes: ","time":1},{"id":4,"question":"What is the difference between the compound interests on Rs. 5000 for 1 years at 4% per annum compounded yearly and half-yearly? ","time":1}]
I solved it using Dictionary instead of using List
private void Answer_Checked(object sender, RoutedEventArgs e) // Radio button click
{
var radio = sender as RadioButton;
bool check = Convert.ToBoolean(radio.IsChecked);
if(check)
{
Answer[Convert.ToInt16(radio.GroupName)] = Convert.ToInt16(radio.Tag);
}
}
public async void Check_Result() // Evaluate result
{
foreach (KeyValuePair<int, int> count in Answer)
{
if (count.Value == 1)
{
result++;
}
}
MessageDialog showresult = new MessageDialog(result.ToString());
await showresult.ShowAsync();
Frame.Navigate(typeof(MainPage), null);
}
public void TestSubmit_Click(object sender, RoutedEventArgs e) // AppBar button click
{
Check_Result();
}

WPF Binding GridView to Element in collection

I'm creating a form that will allows user to add filters to data for processing.
I have setup:
public Class RuleGroup
{
public ObservableCollection<Rule> Rules {get; set;}
...
}
public Class Rule
{
public ObservableCollection<String> Fields {get; set;}
public ObservableCollection<Rule> Rules {get; set;}
...
}
public class Criteria
{
public int ItemId{ get; set;}
public string Field{ get; set;}
public OperationType Operation{ get; set;}
public string Value {get; set;}
public string Value2 {get; set;}
}
So a Rule has a List of Criteria that must be matched if the rule is to be applied. Each Criteria in a Rule must specify a value for every field selected. The Amount of fields may vary from One RuleGroup to the next.
I am trying to set up a form that is user friendly when creating multiple Rules. I was thinking of having a GridView on the form that is some how bound to this class layout.
Rule = Row
Criteria = Column
Currently I have function that generates a DataTable based on the Rules/Criteria as the user move from one RuleGroup to the next, but I think there my be an nicer solution to this
Any ideas or help would be much appreciated
Thanks
Right Think I have got it.
Needed to change my Classes around a bit to get the correct groups / hierarchy. I have then been able to bind the column using the items index in the collection.
This has given me the outcome I wanted, Though there is a minor issue where I would like to be able to access the index using the string Name rather then the position. I am currently having to make sure that the "Criterion" are in the correct order when accessing the values.
Here is a link to the Source code
Rule Group
public class RuleGroup
{
public String Description { get; set; }
public ObservableCollection<Rule> Rules { get; set; }
public RuleGroup()
{
Rules = new ObservableCollection<Rule>();
}
}
Rule
public class Rule
{
public Rule()
{
Criteria = new ObservableCollection<Criteria>();
}
public String Description { get; set; }
public ObservableCollection<Criteria> Criteria { get; set; }
readonly ObservableCollection<RuleField> _Fields = new ObservableCollection<RuleField>();
public IEnumerable<RuleField> Fields
{
get
{
return _Fields;
}
}
public void AddField(string name, string header)
{
if (_Fields.FirstOrDefault(i => i.Name == name) == null)
{
RuleField field = new RuleField(_Fields.Count)
{
Name = name,
Header = header
};
_Fields.Add(field);
AddFieldToCriteria(field);
}
}
void AddFieldToCriteria(RuleField field)
{
foreach (Criteria c in Criteria)
{
if (c.Values.FirstOrDefault(i => i.Field == field) == null)
c.Values.Add(new Criterion() { Field = field, Operation = 1 });
}
}
}
Criteria
public class Criteria
{
public Criteria()
{
Values = new ObservableCollection<Criterion>();
}
public ObservableCollection<Criterion> Values { get; set; }
public Criterion this[int index]
{
get
{
return Values.OrderBy(i=>i.Field.Position).ElementAt(index);
}
set
{
Criterion c = Values.OrderBy(i => i.Field.Position).ElementAt(index);
c= value;
}
}
}
Criterion
public class Criterion
{
public RuleField Field { get; set; }
public int Operation { get; set; }
public object Value { get; set; }
public object Value2 { get; set; }
}
RuleField
public class RuleField
{
public string Name { get; set; }
public string Header { get; set; }
int _Position = 0;
public int Position
{
get
{
return _Position;
}
}
public RuleField(int position)
{
_Position = position;
}
}
View Model
public delegate void UpdateColumnsEventHandler(object sender, UpdateColumnsEventArgs e);
public class UpdateColumnsEventArgs
{
public IEnumerable<RuleField> Columns { get; set; }
public UpdateColumnsEventArgs()
{
Columns = new List<RuleField>();
}
public UpdateColumnsEventArgs(IEnumerable<RuleField> columns)
{
Columns = columns;
}
}
public class MainWindowViewModel
{
public event UpdateColumnsEventHandler UpdateColumns;
public ObservableCollection<RuleGroup> RuleGroups { get; set; }
RuleGroup _SelectedRuleGroup = null;
public RuleGroup SelectedRuleGroup
{
get
{
return _SelectedRuleGroup;
}
set
{
if (_SelectedRuleGroup == value)
return;
_SelectedRuleGroup = value;
}
}
public Rule _SelectedRule = null;
public Rule SelectedRule
{
get
{
return _SelectedRule;
}
set
{
if (_SelectedRule == value)
return;
_SelectedRule = value;
if (UpdateColumns != null)
UpdateColumns(this, new UpdateColumnsEventArgs(_SelectedRule.Fields));
}
}
public MainWindowViewModel()
{
RuleGroups = new ObservableCollection<RuleGroup>();
RuleGroup rg = new RuleGroup();
rg.Description = "Rule Group A";
Rule r = new Rule();
r.Description = "Rule 1";
Random random = new Random();
int range = 10000;
for (int x = 0; x < 2000; x++)
{
Criteria c = new Criteria();
c.Values.Add(new Criterion()
{
Field = new RuleField(0)
{
Name = "A",
Header = "A Column"
},
Operation = 1,
Value = "X"
});
c.Values.Add(new Criterion()
{
Field = new RuleField(0)
{
Name = "B",
Header = "B Column"
},
Operation = 1,
Value = x % 10
});
r.Criteria.Add(c);
}
#region Fields
r.AddField("A", "A Column");
r.AddField("B", "B Column");
r.AddField("C", "C Column");
#endregion
rg.Rules.Add(r);
r = new Rule();
r.Description = "Rule 2";
for (int x = 0; x < 1750; x++)
{
r.Criteria.Add(new Criteria());
}
#region Fields
r.AddField("A", "A Column");
r.AddField("B", "B Column");
#endregion
rg.Rules.Add(r);
RuleGroups.Add(rg);
}
}
WPF Window
<Window x:Class="RuleMappingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:RuleMappingTest"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainWindowViewModel UpdateColumns="UpdateGridColumns"/>
</Window.DataContext>
<Grid Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding RuleGroups}" SelectedItem="{Binding SelectedRuleGroup}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Grid.Row="1" ItemsSource="{Binding SelectedRuleGroup.Rules}" SelectedItem="{Binding SelectedRule}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<DataGrid x:Name="CriteriaGrid" Grid.Row="2" ItemsSource="{Binding SelectedRule.Criteria}" AutoGenerateColumns="False" >
</DataGrid>
</Grid>
</Window>
WPF Code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void UpdateGridColumns(object sender, UpdateColumnsEventArgs e)
{
CriteriaGrid.Columns.Clear();
foreach (RuleField rf in e.Columns)
{
DataGridTextColumn c = new DataGridTextColumn();
c.Header = rf.Header;
Binding b = new Binding(String.Format("[{0}].Value", rf.Position));
CriteriaGrid.Columns.Add(c);
c.Binding = b;
}
}
}

Categories