Below is the XAML. The data from the object is populating the listview. I just can't get the groups to display. I want to group on a property called workcenter, which isn't displayed in the listview, but it's still part of the class bound to it.
I'm a WPF noob, so forgive me if the answer is right in front of me and I'm missing it. I'm using examples from the web to try to get this to work but it's just not to this point.
This is a mock-up for a project I have to do, so ignore the silly form text and namespace. =)
<Window x:Class="Son_of_a_Batching_WPF_Mock_up.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="You Son of a Batch. I'm Detective John Kimble! I'm A Cop You Idiot!" Height="600" Width="800">
<Window.Resources>
<CollectionViewSource Source="{Binding testDataCollection}"
x:Key="src">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="workcenter" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid x:Name="mainGrid" Background="White">
<ComboBox x:Name="cboWorkCenters" HorizontalAlignment="Left" VerticalAlignment="Top" Width="256" Background="{x:Null}" Margin="12,50,0,0" SelectedIndex="0" FontSize="12pt" Height="27.28">
<ComboBoxItem Content="Group by work center" FontSize="12pt"/>
<ComboBoxItem Content="Group by batch type" FontSize="12pt"/>
</ComboBox>
<Label x:Name="lblFilter" FontSize ="12pt" HorizontalAlignment="Left" Margin="11.473,13.043,0,0" VerticalAlignment="Top" Content="Filter by work center:" Width="252.527" FontWeight="Bold"/>
<ListView x:Name="lvBatches" Margin="12,83,12,12" ItemsSource="{Binding Source = {StaticResource src}}"><!--ItemsSource="{Binding testDataCollection}">-->
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text=" {Binding Path=name}" Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold" Text=" {Binding Path=itemcount}"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.View>
<GridView>
<GridViewColumn x:Name="colID" Header="Batch ID" Width="200" DisplayMemberBinding="{Binding id}"/>
<!--<GridViewColumn x:Name="colWC" Header="Work Center" Width="100" DisplayMemberBinding="{Binding workcenter}"/>-->
<GridViewColumn x:Name="colStart" Header="Start Time" Width="150" DisplayMemberBinding="{Binding start}"/>
<GridViewColumn x:Name="colEnd" Header="End Time" Width="150" DisplayMemberBinding="{Binding end}"/>
<GridViewColumn x:Name="colDur" Header="Duration" Width="100" DisplayMemberBinding="{Binding duration}"/>
<GridViewColumn x:Name="colBatchType" Header="Batch Type" Width="100" DisplayMemberBinding="{Binding batchtype}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Since I can't post images, I'll try to map out how it looks using text skills. What I get is this:
**Batch ID Start Time End Time Duration Batch Type**
-------- ---------- -------- -------- ----------
12344555 7/21/11 7/22/11 100 Loaded
54564564 7/21/11 7/23/11 50 Sequential
12433555 7/21/11 7/22/11 100 Loaded
54564564 7/21/11 7/23/11 50 Sequential
12311555 7/21/11 7/22/11 100 Loaded
54456564 7/21/11 7/23/11 50 Sequential
12344555 7/21/11 7/22/11 100 Loaded
57744564 7/21/11 7/23/11 50 Sequential
12994555 7/21/11 7/22/11 100 Loaded
54500564 7/21/11 7/23/11 50 Sequential
What I want is this, with 332, 404 being the groups.
**Batch ID Start Time End Time Duration Batch Type**
-------- ---------- -------- -------- ----------
**332**
12344555 7/21/11 7/22/11 100 Loaded
12433555 7/21/11 7/22/11 100 Loaded
12311555 7/21/11 7/22/11 100 Loaded
**404**
54564564 7/21/11 7/23/11 50 Sequential
54564564 7/21/11 7/23/11 50 Sequential
54500564 7/21/11 7/23/11 50 Sequential
Here's the code behind. I don't think it'll help. Most of it is just test data to display in the mock-up.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
namespace Son_of_a_Batching_WPF_Mock_up
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
private ObservableCollection<TestData> _testDataCollection = new ObservableCollection<TestData>();
public ObservableCollection<TestData> testDataCollection { get { return _testDataCollection; } }
//public System.ComponentModel.ICollectionView source { get; set; }
public Window1()
{
InitializeComponent();
//this.source.GroupDescriptions.Add(new PropertyGroupDescription("workcenter"));
LoadTestData();
}
private void LoadTestData()
{
//int[] icnt = new int[529];
for (int i = 0; i <= 99; i++)
{
_testDataCollection.Add(new TestData());
}
//for (int i = 0; i < _testDataCollection.Count; i++)
//{
// icnt[int.Parse(_testDataCollection[i].workcenter)] ++;
//}
//this.label1.Content = "332:" + icnt[332].ToString() + "," + "402:" + icnt[402].ToString() + ","+ "404:" + icnt[404].ToString() + ","
// + "522:" + icnt[522].ToString() + ","+ "523:" + icnt[523].ToString() + ","+ "524:" + icnt[524].ToString() + ","
// + "527:" + icnt[527].ToString() + ","+ "528:" + icnt[528].ToString() + ",";
}
}
public class TestData
{
private string[] _WCs = new string[] { "404", "527", "523", "524", "332", "528", "522", "402" };
private string[] _workcenters = new string[1000];
private string _workcenter;
private double _duration = 0;
public string id
{
get { return Guid.NewGuid().ToString(); }
}
public string workcenter
{
get
{
return _workcenter;
}
}
public DateTime start
{
get { return DateTime.Now; }
}
public DateTime end
{
get { return DateTime.Now; }
}
public double duration
{
get
{
return _duration;
}
}
public string batchtype
{
get
{
switch (workcenter)
{
case "332":
case "402":
case "527":
return "Loaded Batch";
case "404":
case "524":
return "Sequential Batch";
case "522":
case "528":
case "523":
return "Supervisor Batch";
default:
return "";
}
}
}
public TestData()
{
RandomizeWCs();
Random rnd = new Random();
_workcenter = _workcenters[rnd.Next(0, 999)];
rnd = new Random();
_duration = rnd.Next(10, 60);
}
private void RandomizeWCs()
{
Random rnd = new Random();
int iIndex, i;
while (_workcenters.Contains(null))
{
iIndex = rnd.Next(1000);
i = rnd.Next(8);
if (_workcenters[iIndex] == null)
{
_workcenters[iIndex] = _WCs[i];
}
}
}
}
}
Unreal: I've been banging my head against the wall for hours and I'm embarrassed to say I solved my own problem. The solution?
<TextBlock FontWeight="Bold" Text=" {Binding Path=name}" Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold" Text=" {Binding Path=itemcount}"/>
Freaking name and itemcount weren't capitalized!!! Give me a second to punch myself in the face...annnnnd...I'm back now. Name and ItemCount are the proper way to code that.
Thanks anyway for the help all who responded.
Related
I have a WPF Grid which is 3 columns wide and 8 rows:
<Window x:Class="Container.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="700" Width="1000">
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="10*" />
</Grid.ColumnDefinitions>
</Grid>
</Window>
I am using this to draw something like this:
Every cell in the first and third columns may have a different number of rectangles. Also, the width of each rectangle may be different and change at run-time. The width will be proportionate to a number (known at run-time and continually-changing).
What is the best way to draw these rectangles?
Here is what I've come up with after about an hour of fiddling (GitHub Repo):
I'm using the MVVM pattern to make the UI as easy as possible. Right now, it just populates with some random data.
The XAML:
<Window
x:Class="BuySellOrders.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BuySellOrders"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Window.DataContext>
<local:MainWindowVm />
</Window.DataContext>
<Grid Margin="15">
<ItemsControl ItemsSource="{Binding Path=Prices}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:PriceEntryVm}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border
Grid.Column="0"
Padding="5"
HorizontalAlignment="Stretch"
BorderBrush="Black"
BorderThickness="1">
<ItemsControl HorizontalAlignment="Right" ItemsSource="{Binding Path=BuyOrders}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:OrderVm}">
<Border
Width="{Binding Path=Qty}"
Margin="5"
Background="red"
BorderBrush="Black"
BorderThickness="1" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<Border
Grid.Column="1"
BorderBrush="Black"
BorderThickness="1">
<TextBlock
Margin="8"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Path=Price}" />
</Border>
<Border
Grid.Column="2"
Padding="5"
BorderBrush="Black"
BorderThickness="1">
<ItemsControl ItemsSource="{Binding Path=SellOrders}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:OrderVm}">
<Border
Width="{Binding Path=Qty}"
Margin="5"
Background="red"
BorderBrush="Black"
BorderThickness="1" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
The view models:
class MainWindowVm : ViewModel
{
public MainWindowVm()
{
var rnd = new Random();
Prices = new ObservableCollection<PriceEntryVm>();
for (int i = 0; i < 8; i++)
{
var entry = new PriceEntryVm();
Prices.Add(entry);
entry.BuyOrders.CollectionChanged += OnOrderChanged;
entry.SellOrders.CollectionChanged += OnOrderChanged;
entry.Price = (decimal)110.91 + (decimal)i / 100;
var numBuy = rnd.Next(5);
for (int orderIndex = 0; orderIndex < numBuy; orderIndex++)
{
var order = new OrderVm();
order.Qty = rnd.Next(70) + 5;
entry.BuyOrders.Add(order);
}
var numSell = rnd.Next(5);
for (int orderIOndex = 0; orderIOndex < numSell; orderIOndex++)
{
var order = new OrderVm();
order.Qty = rnd.Next(70) + 5;
entry.SellOrders.Add(order);
}
}
}
private void OnOrderChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in e.NewItems)
{
var order = item as OrderVm;
if (order.Qty > LargestOrder)
{
LargestOrder = order.Qty;
}
}
}
}
private int _largestOrder;
public int LargestOrder
{
get { return _largestOrder; }
private set { SetValue(ref _largestOrder, value); }
}
public ObservableCollection<PriceEntryVm> Prices { get; }
}
public class PriceEntryVm: ViewModel
{
public PriceEntryVm()
{
BuyOrders = new OrderList(this);
SellOrders = new OrderList(this);
}
private Decimal _price;
public Decimal Price
{
get {return _price;}
set {SetValue(ref _price, value);}
}
public OrderList BuyOrders { get; }
public OrderList SellOrders { get; }
}
public class OrderList : ObservableCollection<OrderVm>
{
public OrderList(PriceEntryVm priceEntry)
{
PriceEntry = priceEntry;
}
public PriceEntryVm PriceEntry { get; }
}
public class OrderVm : ViewModel
{
private int _qty;
public int Qty
{
get { return _qty; }
set { SetValue(ref _qty, value); }
}
}
I had to make some assumptions about the naming of things, but hopefully you should get the basic idea of what's going on.
It's structured as a list of PriceEntry, each of which contains a Price, and a BuyOrders and SellOrders properties.
BuyOrders and SellOrders are just lists of orders that have a Quantity property.
The XAML binds the list of price entries to a template that contains a 3 column grid. The first and 3rd columns of that grid bound to another set of item controls for each list of orders. The template for each order is just a border with a Width bound to the Quantity of the order.
All the binds means that just updating a property, or adding an order to either the buy or sell list of a price entry will automatically propagate to the UI. Adding or removing a PriceEntry will also automatically adjust the UI.
I haven't implemented your automatic scaling yet, but the basic idea would be to use a ValueConverter on the Quantity binding, to make it automatically adjust to the largest order.
As an extra note, it uses this nuget package to provide some of the MVVM boiler-plate code, but you should be able to use anything you want, as long as it gives you INotifyPropertyChanged support.
Here is a bonus screen capture showing the dynamic nature of MVVM updating the UI based on a timer.
This only needed a few lines of code to randomly pick a row, then randomly pick an order on the row, then add or subtract a small random amount from the quantity.
_updateTimer = new DispatcherTimer();
_updateTimer.Tick += OnUpdate;
_updateTimer.Interval = TimeSpan.FromSeconds(0.01);
_updateTimer.Start();
private void OnUpdate(object sender, EventArgs e)
{
var entryIndex = _rnd.Next(Prices.Count);
var entry = Prices[entryIndex];
OrderList list;
list = _rnd.Next(2) == 1 ?
entry.BuyOrders :
entry.SellOrders;
if (list.Any())
{
var order = list[_rnd.Next(list.Count)];
order.Qty += _rnd.Next(0, 8) - 4;
}
}
Right then, here goes....
This is exactly the kind of thing you want to use data-binding for. You can try and do things manually if you like, but your code will quickly become very messy if you do. WPF lets you do things the old-school way (i.e. similar to WinForms et al) but that was really to facilitate porting of legacy code. I won't go into too much detail about MVVM (plenty of info on the net about it), but you can get started by using NuGet to add MVVMLightLibs or some other MVVM framework to your project and then you assign your main window a view model by doing something like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
So now it's time for the view model itself, which is a model of the data structures that you want your view to display:
public class MainViewModel : ViewModelBase
{
public ObservableCollection<PriceLevel> PriceLevels { get; } = new ObservableCollection<PriceLevel>
{
new PriceLevel(110.98, new int[]{ }, new int[]{ }),
new PriceLevel(110.97, new int[]{ }, new int[]{ }),
new PriceLevel(110.96, new int[]{ }, new int[]{ }),
new PriceLevel(110.95, new int[]{ }, new int[]{ 5 }),
new PriceLevel(110.94, new int[]{ }, new int[]{ 3, 8 }),
new PriceLevel(110.93, new int[]{ 8, 3, 5, }, new int[]{ }),
new PriceLevel(110.92, new int[]{ 3 }, new int[]{ }),
new PriceLevel(110.91, new int[]{ }, new int[]{ }),
};
}
public class PriceLevel
{
public double Price { get; }
public ObservableCollection<int> BuyOrders { get; }
public ObservableCollection<int> SellOrders { get; }
public PriceLevel(double price, IEnumerable<int> buyOrders, IEnumerable<int> sellOrders)
{
this.Price = price;
this.BuyOrders = new ObservableCollection<int>(buyOrders);
this.SellOrders = new ObservableCollection<int>(sellOrders);
}
}
If you don't already know, ObservableCollection is very similar to list but it propegrates change notification, so when you make your view display the data in it your GUI will update automatically whenever the list changes. This MainViewModel class contains an ObservableCollection of type PriceLevel, and each PriceLevel contains the price and the lists of buy and sell orders. This means you'll be able to add and remove price points, and also add and remove the orders in the price points, and your front-end will reflect those changes.
So on to the front end itself:
<Window.Resources>
<!-- Style to display order list as horizontal list of red rectangles -->
<Style x:Key="OrderListStyle" TargetType="{x:Type ItemsControl}">
<!-- Set ItemsPanel to a horizontal StackPanel -->
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<!-- Display each item in the order list as a red rectangle and scale x by 8*size -->
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1" Margin="5" >
<Rectangle Width="{Binding}" Height="20" Fill="Red">
<Rectangle.LayoutTransform>
<ScaleTransform ScaleX="8" ScaleY="1" />
</Rectangle.LayoutTransform>
</Rectangle>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Style to make Price cells vertically aligned -->
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- This style centers the column's header text -->
<Style TargetType="DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
</Window.Resources>
<!-- This datagrid displays the main list of PriceLevels -->
<DataGrid ItemsSource="{Binding PriceLevels}" AutoGenerateColumns="False" IsReadOnly="True"
CanUserAddRows="False" CanUserDeleteRows="False" CanUserReorderColumns="False" CanUserResizeColumns="False"
CanUserResizeRows="False" CanUserSortColumns="False" RowHeight="30">
<DataGrid.Columns>
<!-- The buy orders column -->
<DataGridTemplateColumn Header="Buy orders" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding BuyOrders}" Style="{StaticResource OrderListStyle}" HorizontalAlignment="Right" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- The price column -->
<DataGridTextColumn Header="Price" Width="Auto" Binding="{Binding Price}" />
<!-- The sell orders column -->
<DataGridTemplateColumn Header="Sell Orders" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding SellOrders}" Style="{StaticResource OrderListStyle}" HorizontalAlignment="Left" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Looks a bit full-on, but if you break it down into sections it's actually pretty straightforward. The main different between this and what you've been trying to do is that I'm using a DataGrid. This is basically a Grid control that's had extra functionality added to make it respond dynamically to data that it's been bound to. It also has a lot of extra stuff we dont' need (editing, column resize/reordering etc) so I've turned all that off. The DataGrid binds to PriceLevels in the view model, so it will display a vertical list showing each one. I've then explicitly declared the 3 columns you're after. The middle one is easy, it's just text, so DataGridTextColumn will, do the job. The other two are horizontal arrays of rectangles, so I've used DataGridTemplateColumn which allows me to customize exactly how they look. This customization is mostly done in the OrderListStyle at the very top of the XAML which sets ItemsPanel to a horizontal StackPanel and sets ItemTemplate to a rectangle. There's also a bit of XAML in there to scale the rectangle by a constant, according to the value of the integer it's displaying in the order list.
Here's the result:
I know the XAML might seem a little full-on, but keep in mind this is now fully data-bound to that view model and it will automatically update in response to changes. This little bit of extra work at the start results in MUCH cleaner update code which is also easier to test and debug.
Hope this is what you're after, if you have any questions let me know and we can take it into chat.
UPDATE: If you want to see the dynamic update in action then add this to your main view model's constructor, it just adds and removes orders randomly:
public MainViewModel()
{
var rng = new Random();
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(0.1);
timer.Tick += (s, e) =>
{
var row = this.PriceLevels[rng.Next(this.PriceLevels.Count())]; // get random row
switch (rng.Next(4))
{
case 0: row.BuyOrders.Add(1 + rng.Next(5)); break;
case 1: row.SellOrders.Add(1 + rng.Next(5)); break;
case 2: if (row.BuyOrders.Count() > 0) row.BuyOrders.RemoveAt(rng.Next(row.BuyOrders.Count())); break;
case 3: if (row.SellOrders.Count() > 0) row.SellOrders.RemoveAt(rng.Next(row.SellOrders.Count())); break;
}
};
timer.Start();
}
I have task to create in C# UWP user created check-list.
But I have stuck from the beginning cause XAML is new for me, so I have no idea what to start from.
So, I have textbox to enter title, task or subtask to in listbox (priviously added to) selected task.
this is my xaml how it looks like now:
<Page
x:Class="Table1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Table1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<TextBox x:Name="txt" HorizontalAlignment="Left" Height="71" Margin="71,247,0,0" Text="TextBox" VerticalAlignment="Top" Width="395"/>
<RadioButton x:Name="title" Content="Add Title" HorizontalAlignment="Left" Margin="71,86,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="task" Content="Add Task" HorizontalAlignment="Left" Margin="71,123,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="subtask" Content="Add Subtask" HorizontalAlignment="Left" Margin="71,155,0,0" VerticalAlignment="Top"/>
<ListBox x:Name="listbox" HorizontalAlignment="Left" Height="68" Margin="71,354,0,0" VerticalAlignment="Top" Width="395"/>
<Button x:Name="btn" Content="Button" HorizontalAlignment="Left" Margin="401,483,0,0" VerticalAlignment="Top" Click="btn_Click"/>
</Grid>
</Page>
There are the code:
public class subtasks
{
public string parent { get; set; }
public string subtask { get; set; }
public subtasks(string parenti, string subtaski)
{
parent = parenti;
subtask = subtaski;
}
public void setsub(string parenti, string sub)
{
parent = parenti;
subtask = sub;
}
}
List<string> Tasks = new List<string>();
List<subtasks> sub = new List<subtasks>();
private void btn_Click(object sender, RoutedEventArgs e)
{
string parent = "";
string Title;
string Task;
string Subtask;
if (title.IsChecked==true)
{
Title = txt.Text;
adding(Title, parent, 1);
}
else if (task.IsChecked==true)
{
Task = txt.Text;
adding(Task, parent, 2);
}
else if (subtask.IsChecked==true)
{
parent = listbox.SelectedItem.ToString();
Subtask = txt.Text;
adding(Subtask, parent, 3);
}
else
{
}
}
private void adding(string str, string par, int x)
{
subtasks subi = new subtasks(par,str);
RowDefinition row = new RowDefinition();
TextBlock text = new TextBlock();
if (x==1)
{
print(str);
}
else if (x==2)
{
Tasks.Add(str);
listbox.Items.Add(str);
text.Text = str;
print(str);
}
else
{
sub.Add(subi);
print(str);
}
}
private void print(string title)
{
int step = 0;
Grid gridwin = new Grid();
gridwin.Children.Clear();
RowDefinition row = new RowDefinition();
TextBlock text = new TextBlock();
text.Text = title;
Grid.SetColumn(text, 0);
Grid.SetRow(text, step);
step++;
for (int i = 0; i < Tasks.Count; i++)
{
text.Text = Tasks[i].ToString();
gridwin.Children.Add(text);
Grid.SetColumn(text, 0);
Grid.SetRow(text, step);
step++;
for (int k = 0; k < sub.Count; k++)
{
if (sub[k].parent == Tasks[i])
{
text.Text = sub[k].subtask.ToString();
gridwin.Children.Add(text);
Grid.SetColumn(text, 0);
Grid.SetRow(text, step);
step++;
}
}
}
}
As you see I need to clear and put data every time the button is clicked, cause you never know when user will decide to add new subtask for previously added task. So, the question is, how to make the table with column1 with tasks and subtasks and column2 which is chekbox.
What you want to probably do is to create a DataTemplate. You use this to specify how list items should be displayed and formatted. This way you can specify you want to lay them out as a Grid with two columns like description and CheckBox. Take a look into the documentation to see some examples of DataTemplates. You can also see the Azure Mobile Apps quickstart for UWP, because although it is focused on demonstrating Microsoft Azure integration to UWP, it is actually a to-do app, which should give you some inspiration for building your own.
The layout could look like this:
<ListBox x:Name="listbox" HorizontalAlignment="Left" Height="68" Margin="71,354,0,0" VerticalAlignment="Top" Width="395">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Text}" />
<CheckBox Grid.Column="1" IsChecked="{Binding IsChecked, Mode=TwoWay}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You can see my code is also using {Binding} syntax, which you will also need to learn a bit about to be able to know when the user has checked a to-do item in the list. I suggest you to take a look at a simple tutorial sample like here. In fact, data-binding is one of the most important things when building XAML-based apps and when you get to understand this concept, it will help you a lot on the way to becoming a UWP ninja :-) .
Why dont use the UWP DataGrid with CheckBox?
XAML
<toolkit:DataGrid Grid.Column="0" ItemsSource="{x:Bind myItemsToBind}"
x:Name="dgwDeviceSPNs" MinWidth="100"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Visible"
AlternatingRowBackground="Transparent"
AreRowDetailsFrozen="False"
AreRowGroupHeadersFrozen="True"
AutoGenerateColumns="False"
CanUserSortColumns="False"
CanUserReorderColumns="True"
RowGroupHeaderPropertyNameAlternative=""
CanUserResizeColumns="True"
MaxColumnWidth="200"
FrozenColumnCount="0"
GridLinesVisibility="Horizontal"
HeadersVisibility="None"
IsReadOnly="True"
RowDetailsVisibilityMode="Collapsed"
SelectionMode="Single">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTemplateColumn MinWidth="10">
<toolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Padding="2">
<CheckBox ToolTipService.ToolTip="{Binding Name}" IsChecked="{Binding IsSelected, Mode=TwoWay}" Content="{Binding Name}"></CheckBox>
</StackPanel>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
</toolkit:DataGridTemplateColumn>
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
Please have a look at this image to understand my scenario
So I have the above scenario in my Windows Phone application where I have a ListBox with the below layout.
My XAML for the ListBox
<ListBox x:Name="llsIceCreamBrands" Margin="0,54,0,0" CacheMode="BitmapCache">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,40">
<StackPanel>
<Border BorderBrush="{StaticResource PhoneProgressBarBackgroundBrush}" BorderThickness="3">
<Image Width="200" Height="200" Source="{Binding IceCreamBrandImage }" Margin="3,0,0,0" Stretch="Fill"/>
</Border>
</StackPanel>
<StackPanel>
<Grid Margin="20,-5,0,0" Height="250" >
<StackPanel>
<TextBlock Text="{Binding IceCreamBrandName }" FontWeight="SemiBold" FontSize="34" FontFamily="Segoe WP" Margin="0" TextWrapping="Wrap" Width="184" VerticalAlignment="Top" HorizontalAlignment="Left" Opacity="1" LineStackingStrategy="BlockLineHeight" LineHeight="35" Height="Auto" />
<TextBlock Text="{Binding IceCreamBrandFlavour }" FontWeight="Normal" FontSize="15" FontFamily="Segoe WP" TextWrapping="Wrap" VerticalAlignment="Top" Width="200" Opacity="0.7" HorizontalAlignment="Left" IsHitTestVisible="False" Height="140"/>
</StackPanel>
</Grid>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In C# Code:
void GetIceCreamBrands()
{
string mystring = "";
for (int i = 0; i < dc.IceCreams[cID].Brands.Count; i++)
{
var bmp = new BitmapImage();
if (dc.IceCreams[cID].Brands[i].HasArt)
{
bmp.SetSource(dc.IceCreams[cID].Brands[i].GetImage());
}
else
{
bmp.CreateOptions = BitmapCreateOptions.None;
bmp.UriSource = new Uri("/Assets/Images/IceCreams/Placeholder.png", UriKind.Relative);
}
for (int j = 0; j < dc.IceCreams[cID].Brands.Count; j++)
{
for (int k = 0; k < dc.IceCreams[cID].Brands[j].Flavour.Count; k++)
{
sourceFlavourList.Add(new Flavour
{
FlavourBrand = dc.IceCreams[cID].Brands[j].Name,
BrandFlavourName = dc.IceCreams[cID].Brands[j].Flavour[k].Name
});
}
}
sourceIceCreamBrands.Add(new IceCreamBrands
{
IceCreamBrandName = dc.IceCreams[cID].Brands[i].Name,
IceCreamBrandImage = bmp,
IceCreamBrandFlavour = "Get Flavours here for the particular brand somehow?"
});
}
llsIceCreamBrands.ItemsSource = sourceIceCreamBrands.ToList();
}
I have the above method in my code which successfully gets the brands and their images, but the flavours are also a Collection which I tried to get in a single string by using
string mystring = string.Join(Environment.NewLine, sourceFlavourList.Select(x => x.IceCreamBrandFlavourName));
But this returns the same values for both Ben&Jerrys and Haagen-Dazs - which is an amalgamation of both brands' flavours.
How can I achieve what I'm looking for?
I don't see where you define the variable 'sourceFlavourList' within GetIceCreamBrands(), so I assume you have this defined elsewhere in your code. This would explain why you see the full list of flavours (from both brands) as you keep adding all flavours to this one variable.
So instead of using this full list variable, you would need to bind to a per brand list (which you DO have in your result)., so you can use the same linq you have, but with a different source variable).
Try something like this:
var flavours = string.Join(Environment.NewLine, dc.IceCreams[cID].Brands[j].Flavour.Select(x => x.Name));
sourceIceCreamBrands.Add(new IceCreamBrands
{
IceCreamBrandName = dc.IceCreams[cID].Brands[i].Name,
IceCreamBrandImage = bmp,
IceCreamBrandFlavour = flavours
});
So I am hoping to alter my program such that I can run a function to check and see if the foreground color should be black or silver. I am hoping to gray out fields that are not "accessible".
My form currently looks like:
I was hoping to "gray out" the "No maintenance required" fields. But I am having problems with trying to define a binding element to the font foreground in my data template.
I've tried everything from trying to define an IValueConverter class within the main window code behind, to defining a window key resource, but it doesn't appear that I can do that within a data template on the textblock element itself?
Any suggestions/help would be appreciated. Thanks!
XAML:
<Grid Margin="0,0,2,0">
<ListBox x:Name="allSites_LB"
HorizontalAlignment="Left"
Height="400"
Margin="20,60,0,0"
VerticalAlignment="Top"
Width="945"
Loaded="allSites_LB_Loaded"
BorderThickness="1" SelectionChanged="allSites_LB_SelectionChanged"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
>
<ListBox.ItemTemplate >
<DataTemplate >
<Border BorderBrush="Black" BorderThickness="0,0,0,1" Margin="-20,1,0,1" Padding="0,5,0,5" >
<Grid Margin="75,3" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="400" />
<ColumnDefinition Width="345" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding SiteNo}" Grid.Column="0" FontSize="16" />
<TextBlock Text="{Binding Address}" Grid.Column="1" FontSize="16" Margin="50,1" />
<TextBlock Text="{Binding MaintStatus}" Grid.Column="2" FontSize="16" />
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="viewHistory_BTN"
Content="View History"
HorizontalAlignment="Left"
Height="52"
Margin="20,496,0,0"
VerticalAlignment="Top"
Width="172" FontSize="20"
/>
<Button x:Name="startMaintenance_BTN"
Content="Start Maintenance"
HorizontalAlignment="Left"
Height="52"
Margin="793,496,0,0"
VerticalAlignment="Top"
Width="172" FontSize="20"
/>
<TextBox x:Name="Site_Address"
HorizontalAlignment="Left"
Height="21"
Margin="51,39,0,0"
TextWrapping="Wrap"
Text="Site Number"
VerticalAlignment="Top"
Width="75"
BorderBrush="White"
IsReadOnly="True"
IsEnabled="False"
/>
<TextBox x:Name="Address_Title"
HorizontalAlignment="Left"
Height="21"
Margin="380,34,0,0"
TextWrapping="Wrap"
Text="Address"
VerticalAlignment="Top"
Width="75"
BorderBrush="White"
IsReadOnly="True"
IsEnabled="False"
/>
<TextBox x:Name="maint_Title"
HorizontalAlignment="Left"
Height="21"
Margin="699,34,0,0"
TextWrapping="Wrap"
Text="Maintenance Record"
VerticalAlignment="Top"
Width="117"
BorderBrush="White"
IsReadOnly="True"
IsEnabled="False"
/>
</Grid>
C# Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data.SqlClient;
namespace SiteMaintenance
{
public partial class MainWindow : Window
{
/**
* CLASS VARIABLES
* */
private SqlConnection localdbConnection; // Connection to Site Maintenance DB (LOCAL)
private System.Data.DataSet allSitesResults;
// MAIN THREAD
public MainWindow()
{
InitializeComponent();
// try to open SQL Connection
try {
localdbConnection = new SqlConnection(Properties.Settings.Default.localdb);
localdbConnection.Open();
} catch(Exception ex) {
System.Windows.MessageBox.Show("local SQL connection unable to connect");
return;
}
viewHistory_BTN.IsEnabled = false;
startMaintenance_BTN.IsEnabled = false;
startMaintenance_BTN.IsDefault = true;
}
/**
* Load dataset into datagrid
* LAZY LOADING
* */
private void DataGrid_Loaded(object sender, RoutedEventArgs e)
{
// init command object
SqlCommand myCommand = new SqlCommand();
myCommand.CommandText = "dbo.usp_GetSites";
myCommand.CommandType = System.Data.CommandType.StoredProcedure;
myCommand.Connection = localdbConnection;
// init data adaptor
SqlDataAdapter sites = new SqlDataAdapter();
sites.SelectCommand = myCommand;
//init DataSet
allSitesResults = new System.Data.DataSet();
sites.Fill(allSitesResults, "tblSites");
int tableCount = allSitesResults.Tables.Count;
System.Data.DataTable test = allSitesResults.Tables[0];
int rowCount = test.Rows.Count;
}
private void sites_DG_CurrentCellChanged(object sender, EventArgs e)
{
String siteName = allSitesResults.Tables[0].Rows[0][1].ToString();
}
private void allSites_LB_Loaded(object sender, RoutedEventArgs e)
{
// init command object
SqlCommand myCommand = new SqlCommand();
myCommand.CommandText = "dbo.usp_GetSitesANDCompletedDate";
myCommand.CommandType = System.Data.CommandType.StoredProcedure;
myCommand.Connection = localdbConnection;
// init data adaptor
SqlDataAdapter sites = new SqlDataAdapter();
sites.SelectCommand = myCommand;
//init DataSet
allSitesResults = new System.Data.DataSet();
sites.Fill(allSitesResults, "tblSites");
allSites_LB.ItemsSource = allSitesResults.Tables["tblSites"].DefaultView;
}
// do not allow selection of maintenance records that do not exist
private void allSites_LB_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// grab the index
int selectedIndex = allSites_LB.SelectedIndex;
if (selectedIndex == -1) return; //WITHOUT THIS CHECK, UN-SELECTION WILL CAUSE LOGIC FAILURE
System.Data.DataRowView tempData = (System.Data.DataRowView)allSites_LB.Items[allSites_LB.SelectedIndex];
// grab the completed date field
String completedDate = tempData["CompletedDate"].ToString();
String siteMaintID = tempData["SiteMaintID"].ToString();
// remove selected index if completed date and site Maint ID is null
if (siteMaintID != "" && completedDate == "")
{
startMaintenance_BTN.IsEnabled = true;
}
else
{
allSites_LB.SelectedIndex = -1;
startMaintenance_BTN.IsEnabled = false;
}
}
private String maintRequired(object sender, SelectionChangedEventArgs e)
{
int selectedIndex = allSites_LB.SelectedIndex;
if (selectedIndex < 0) return null;
System.Data.DataRowView tempData = (System.Data.DataRowView)allSites_LB.Items[allSites_LB.SelectedIndex];
// grab the completed date field
String completedDate = tempData["CompletedDate"].ToString();
String siteMaintID = tempData["SiteMaintID"].ToString();
if (siteMaintID != "" && completedDate == "")
{
return "Maintenance Required";
}
else
{
return "No Maintenance";
}
}
}
}
There are generally two good approaches for you to choose from, when binding the Foreground color to a piece of data. Depending on who you ask, different people will have different preferences. So... here's both!
First Method: Style with Triggers
This method basically identifies 'special' behavior when a certain set of conditions are met. In this case, we're changing the foreground color to Gray, when the status == "No Maintenance Required"
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Black" /> <!-- default value -->
<Style.Triggers>
<DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Self}" Value="No Maintenance Required">
<Setter Property="Foreground" Value="Gray" /> <!-- special behavior -->
</DataTrigger>
</Style.Triggers>
</Style>
In this case, just assign your TextBlock the appropriate Style property.
Second Method: Use an IValueConverter
This approach creates a custom "IValueConverter implementation, which converts your Text value to a Color. From there, we bind directly to our text value, and ensure that the converter always provides the proper color.
public class MaintenaceColorConverter : IValueConverter
{
public Color NormalColor { get; set; }
public Color NoMaintenanceRequiredColor { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value.ToString() == "No Maintenance Required")
return NoMaintenanceRequiredColor;
return NormalColor;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
In your XAML:
<Window.Resources>
<local:MaintenaceColorConverter x:Key="myColorConverter" NormalColor="Black" NoMaintenanceRequiredColor="Gray" />
</Window.Resources>
In your TextBlock:
<TextBlock Text="{Binding MaintStatus}" Foreground="{Binding MaintStatus, Converter={StaticResource myColorConverter}}" />
Improvements
With either of these approaches, it would be better to have a MaintenanceStatus boolean or enum value, and bind your styling conditions to that. It's a bad idea to use string-comparisons. That's just begging for trouble. These examples used string comparison because... well... that's all that was available from your provided example code.
More than you asked for but this is from some existing code
<Style TargetType="ListViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsMaintenance}" Value="True">
<Setter Property="Background" Value="Gainsboro" />
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
Thanks for the feedback! I went with BTownTKD's suggestion on implementing an IValueConverter although with some alterations in my code. I discovered I needed to define "local" scope in my window properties in XAML.
Also, I was discovering that the binding wasn't actually changing the text color. After stepping through the code and seeing that the method was being properly invoked, I then hardcoded the results returned into the XAML to make sure they were working (foreground="black" or foreground="#FF00000"). I noticed when stepping through the code that the return object was a "color" object in the original binding, and by me hard-coding the colors into the XAML, they were actually strings. So I altered the code slightly to add in a .ToString() to the object I was returning and VOILA it worked! Thanks again for the help!
FYI here's the updated code bits:
XAML:
<Window x:Class="SiteMaintenance.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SiteMaintenance"
Title="MainWindow"
Height="600"
Width="1000">
<Window.Resources>
<local:MaintenenceColorConverter x:Key="MyColorConverter" NormalColor="Black" NoMaintenanceRequiredColor="Gray" />
</Window.Resources>
<Grid Margin="0,0,2,0">
<ListBox x:Name="allSites_LB"
HorizontalAlignment="Left"
Height="400"
Margin="20,60,0,0"
VerticalAlignment="Top"
Width="945"
Loaded="allSites_LB_Loaded"
BorderThickness="1" SelectionChanged="allSites_LB_SelectionChanged"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
>
<ListBox.ItemTemplate >
<DataTemplate >
<Border BorderBrush="Black" BorderThickness="0,0,0,1" Margin="-20,1,0,1" Padding="0,5,0,5" >
<Grid Margin="75,3" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="400" />
<ColumnDefinition Width="345" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding SiteNo}" Grid.Column="0" FontSize="16" Foreground="{Binding MaintStatus, Converter={StaticResource MyColorConverter}}" />
<TextBlock Text="{Binding Address}" Grid.Column="1" FontSize="16" Margin="50,1" Foreground="{Binding MaintStatus, Converter={StaticResource MyColorConverter}}" />
<TextBlock Text="{Binding MaintStatus}" Grid.Column="2" FontSize="16" Foreground="{Binding MaintStatus, Converter={StaticResource MyColorConverter}}" />
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="viewHistory_BTN"
Content="View History"
HorizontalAlignment="Left"
Height="52"
Margin="20,496,0,0"
VerticalAlignment="Top"
Width="172" FontSize="20"
/>
<Button x:Name="startMaintenance_BTN"
Content="Start Maintenance"
HorizontalAlignment="Left"
Height="52"
Margin="793,496,0,0"
VerticalAlignment="Top"
Width="172" FontSize="20"
/>
<TextBox x:Name="Site_Address"
HorizontalAlignment="Left"
Height="21"
Margin="51,39,0,0"
TextWrapping="Wrap"
Text="Site Number"
VerticalAlignment="Top"
Width="75"
BorderBrush="White"
IsReadOnly="True"
IsEnabled="False"
/>
<TextBox x:Name="Address_Title"
HorizontalAlignment="Left"
Height="21"
Margin="380,34,0,0"
TextWrapping="Wrap"
Text="Address"
VerticalAlignment="Top"
Width="75"
BorderBrush="White"
IsReadOnly="True"
IsEnabled="False"
/>
<TextBox x:Name="maint_Title"
HorizontalAlignment="Left"
Height="21"
Margin="699,34,0,0"
TextWrapping="Wrap"
Text="Maintenance Record"
VerticalAlignment="Top"
Width="117"
BorderBrush="White"
IsReadOnly="True"
IsEnabled="False"
/>
</Grid>
</Window>
C# Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data.SqlClient;
namespace SiteMaintenance
{
public partial class MainWindow : Window
{
/**
* CLASS VARIABLES
* */
private SqlConnection localdbConnection; // Connection to Site Maintenance DB (LOCAL)
private System.Data.DataSet allSitesResults;
// MAIN THREAD
public MainWindow()
{
InitializeComponent();
// try to open SQL Connection
try {
localdbConnection = new SqlConnection(Properties.Settings.Default.localdb);
localdbConnection.Open();
} catch(Exception ex) {
System.Windows.MessageBox.Show("local SQL connection unable to connect");
return;
}
viewHistory_BTN.IsEnabled = false;
startMaintenance_BTN.IsEnabled = false;
startMaintenance_BTN.IsDefault = true;
}
/**
* Load dataset into datagrid
* LAZY LOADING
* */
private void DataGrid_Loaded(object sender, RoutedEventArgs e)
{
// init command object
SqlCommand myCommand = new SqlCommand();
myCommand.CommandText = "dbo.usp_GetSites";
myCommand.CommandType = System.Data.CommandType.StoredProcedure;
myCommand.Connection = localdbConnection;
// init data adaptor
SqlDataAdapter sites = new SqlDataAdapter();
sites.SelectCommand = myCommand;
//init DataSet
allSitesResults = new System.Data.DataSet();
sites.Fill(allSitesResults, "tblSites");
int tableCount = allSitesResults.Tables.Count;
System.Data.DataTable test = allSitesResults.Tables[0];
int rowCount = test.Rows.Count;
}
private void sites_DG_CurrentCellChanged(object sender, EventArgs e)
{
String siteName = allSitesResults.Tables[0].Rows[0][1].ToString();
}
private void allSites_LB_Loaded(object sender, RoutedEventArgs e)
{
// init command object
SqlCommand myCommand = new SqlCommand();
myCommand.CommandText = "dbo.usp_GetSitesANDCompletedDate";
myCommand.CommandType = System.Data.CommandType.StoredProcedure;
myCommand.Connection = localdbConnection;
// init data adaptor
SqlDataAdapter sites = new SqlDataAdapter();
sites.SelectCommand = myCommand;
//init DataSet
allSitesResults = new System.Data.DataSet();
sites.Fill(allSitesResults, "tblSites");
allSites_LB.ItemsSource = allSitesResults.Tables["tblSites"].DefaultView;
}
// do not allow selection of maintenance records that do not exist
private void allSites_LB_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// grab the index
int selectedIndex = allSites_LB.SelectedIndex;
if (selectedIndex == -1) return; //WITHOUT THIS CHECK, UN-SELECTION WILL CAUSE LOGIC FAILURE
System.Data.DataRowView tempData = (System.Data.DataRowView)allSites_LB.Items[allSites_LB.SelectedIndex];
// grab the completed date field
String completedDate = tempData["CompletedDate"].ToString();
String siteMaintID = tempData["SiteMaintID"].ToString();
// remove selected index if completed date and site Maint ID is null
if (siteMaintID != "" && completedDate == "")
{
startMaintenance_BTN.IsEnabled = true;
}
else
{
allSites_LB.SelectedIndex = -1;
startMaintenance_BTN.IsEnabled = false;
}
}
private String maintRequired(object sender, SelectionChangedEventArgs e)
{
int selectedIndex = allSites_LB.SelectedIndex;
if (selectedIndex < 0) return null;
System.Data.DataRowView tempData = (System.Data.DataRowView)allSites_LB.Items[allSites_LB.SelectedIndex];
// grab the completed date field
String completedDate = tempData["CompletedDate"].ToString();
String siteMaintID = tempData["SiteMaintID"].ToString();
if (siteMaintID != "" && completedDate == "")
{
return "Maintenance Required";
}
else
{
return "No Maintenance";
}
}
}
public class MaintenenceColorConverter : IValueConverter
{
public Color NormalColor { get; set; }
public Color NoMaintenanceRequiredColor { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value.ToString() == "No Maintenance Required") return NoMaintenanceRequiredColor.ToString();
return NormalColor.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
I'll be cleaning up my code later with BTown's optimization, but at least its working!
so i am working on a Windows Phone 7 application, and i am having a problem, normally in my other WPF/WinForm applications this code would work but here on Wphone 7 i am receiving a problems, i created data class:
public class AlarmTemplate
{
public string Name { get; set; }
public string Time { get; set; }
public BitmapImage Activated { get; set; }
public AlarmTemplate(string name, string time, string activated)
{
Name = name;
Time = time;
Activated = new BitmapImage
{UriSource = new Uri("Images/alarm_" + activated + ".png", UriKind.RelativeOrAbsolute)};
}
}
Next thing read dad, also i tried with hard coding data and its not working:
private List<AlarmTemplate> _templateList = new List<AlarmTemplate>();
private void PopulateList()
{
using (var storage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!storage.FileExists("file.txt"))
return;
using (var reader = new BinaryReader(storage.OpenFile("file.txt", FileMode.Open)))
{
var s = reader.ReadInt32();
for (var i = 0; i < s; i++)
{
_templateList.Add(new AlarmTemplate(reader.ReadString(), reader.ReadString(),
reader.ReadString()));
}
}
}
lbAlarms.ItemsSource = _templateList;
}
Here is xaml:
<ListBox Height="176.135" HorizontalAlignment="Left" Margin="0,567.164,0,0" Name="lbAlarms" VerticalAlignment="Top" Width="456" Foreground="#FFFFC7C7" ItemsSource="{Binding}" Background="Transparent" AllowDrop="False" BorderThickness="1" BorderBrush="#00900707" Grid.Row="1" Hold="lbAlarms_Hold">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Grid.Row="1" Height="52" Orientation="Horizontal" Name="spList" VerticalAlignment="Top" Width="480" Margin="0,329,0,0" UseLayoutRounding="False">
<Image Height="52" Name="imTStatus" Stretch="Uniform" Width="73" Margin="10,0,0,0" UseLayoutRounding="False" Source="{Binding Activated}" />
<StackPanel Height="52" Name="spHolder" Width="300" Margin="10,0,0,0" VerticalAlignment="Stretch" HorizontalAlignment="Left" UseLayoutRounding="False">
<TextBlock Height="26" Name="tbTTime" Text="{Binding Time}" Foreground="Black" FontFamily=".\Fonts\Nokia.ttf#Nokia" TextAlignment="Left" FontWeight="Bold" Width="230" FontSize="24" HorizontalAlignment="Left" UseLayoutRounding="False" />
<TextBlock Height="26" Name="tbTName" Text="{Binding Name}" Foreground="Black" FontFamily=".\Fonts\Nokia.ttf#Nokia" HorizontalAlignment="Left" Width="297" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Your code is working (I noticed the scrollbar was present on the right), but your text colour is black on black so not very visible.
Get rid of the TextBlock colour Foreground="Black" :)
Note, your margin means your listbox is very small at the bottom of the page, less than 1 item high, so you might want to change that as well.
private List<AlarmTemplate> _templateList = new List<AlarmTemplate>();
public List<AlarmTemplate> TemplateList
{
get { return _templateList; }
set { _templateList = value; }
}
and set the binding to TemplateList.