UWP/C# ItemsControl Multiple Boxes? - xaml

I have had a lot of help so far with creating the correct formatting for an ItemsControl panel and appreciate this communities help so far with helping me troubleshoot coding issues.
Im currently at a rather small hurdle where im trying to figure out how to create multiple boxes within the same ItemsControl. Currently the overall view looks like this:
Im a little stumped and would just like a little guidance really as to where to put the other XAML lines.
I need it to look like this:
Here is my code currently (its all nested within a Frame):
<ItemsControl ItemsSource="{StaticResource userDataCollection}" Margin="40,40,40,725" Width="Auto" Height="310">
<!-- Changing Orientation of VirtualizingStackPanel -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Change header for ItemsControl -->
<ItemsControl.Template>
<ControlTemplate>
<Border Background="{StaticResource CustomAcrylicDarkBackground}">
<StackPanel>
<TextBlock Text="Accounts At A Glance" FontSize="28" Foreground="#b880fc" Padding="12"/>
<ItemsPresenter/>
</StackPanel>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<!-- Template for each card-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="240" Height="240" Background="Gray" Margin="30,0,0,0" VerticalAlignment="Center" Padding="4">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Name}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="24"/>
<TextBlock Grid.Row="1" Text="{Binding PayDate}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="14" />
<TextBlock Grid.Row="2" Text="{Binding NumberOfItems}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="14"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I really apologise for this, im trying to learn as much as i can as i go. Im mainly stuggling with the XAML formatting and incorperating learning material into my project :/ Any help would be amazing

I have an alternative approach for your problem. This uses "semi" MVVM approach (browse through the net and study this pattern).
MainPageViewModel.cs
public class MainPageViewModel : INotifyPropertyChanged
{
private ObservableCollection<User> _userCollection;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<User> UserCollection
{
get => _userCollection;
set
{
_userCollection = value;
NotifyProperyChanged();
}
}
private void NotifyProperyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void LoadData()
{
UserCollection = new ObservableCollection<User>
{
new User
{
Name = "John Doe",
PayDate = DateTime.Now,
NumberOfItems = 1
},
new User
{
Name = "John Doe 2",
PayDate = DateTime.Now,
NumberOfItems = 1
},
new User
{
Name = "John Doe 3",
PayDate = DateTime.Now,
NumberOfItems = 1
},
};
}
}
The view (got rid of some styling temporarily):
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:App1.ViewModels"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Loaded="MainPage_OnLoaded">
<Page.DataContext>
<vm:MainPageViewModel/>
</Page.DataContext>
<Grid>
<ScrollViewer>
<ItemsControl ItemsSource="{Binding UserCollection, Mode=TwoWay}" Margin="15" Width="Auto" Height="310">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Template for each card-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="200" Height="100" Background="Gray" Margin="15,0,0,0" VerticalAlignment="Center" Padding="4">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Name}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="24"/>
<TextBlock Grid.Row="1" Text="{Binding PayDate}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="14" />
<TextBlock Grid.Row="2" Text="{Binding NumberOfItems}" HorizontalAlignment="Center" TextAlignment="Center" Width="220" FontSize="14"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Page>
View's code-behind:
namespace App1
{
public sealed partial class MainPage
{
public MainPage()
{
this.InitializeComponent();
}
public MainPageViewModel VM => (MainPageViewModel) DataContext;
private void MainPage_OnLoaded(object sender, RoutedEventArgs e)
{
VM.LoadData();
}
}
}
Output:
Next steps:
Apply your styling. Study how to limit grid columns.
Improve the code
further, in MVVM you shouldn't really have implementations on the
code-behind, so study for ICommand, Microsoft.Xaml.Interactivity
Hope this helps.

It perfect now.
Im an idiot.
I essentially needed to seperate the information presented within the UserData.cs Class. I didnt understand how the information was being read but understand it now. The XAML has been left untouched as it works currently for what i need. It will be update to follow the MVVM format as mentioned by Mac. Here is the UserData.CS class located inside a data folder:
using System.Collections.ObjectModel;
namespace BudgetSheet.Data
{
public class UserData
{
public string Name { get; set; }
public string PayDate { get; set; }
public string NumberOfItems { get; set; }
}
class UserDataCollection : ObservableCollection<UserData>
{
public UserDataCollection()
{
// Placeholder, needs to be replaced with CSV or Database information
this.Add(new UserData()
{
Name = "Selected Username",
PayDate = "Friday",
NumberOfItems = "ItemAmount Placeholder"
});
// Placeholder for user 2
this.Add(new UserData()
{
Name = "Selected Username 2",
PayDate = "Friday 2",
NumberOfItems = "ItemAmount Placeholder 2"
});
// Placeholder for user 3
this.Add(new UserData()
{
Name = "Selected Username 3",
PayDate = "Friday 3",
NumberOfItems = "ItemAmount Placeholder 3"
});
}
}
}
Here is what it was before hand and why there were issues with information display:
using System.Collections.ObjectModel;
namespace BudgetSheet.Data
{
public class UserData
{
public string Name { get; set; }
public string PayDate { get; set; }
public string NumberOfItems { get; set; }
}
class UserDataCollection : ObservableCollection<UserData>
{
public UserDataCollection()
{
// Placeholder, needs to be replaced with CSV or Database information
this.Add(new UserData()
{
Name = "Selected Username",
});
// Placeholder for user selected PayDate
this.Add(new UserData()
{
PayDate = "Friday",
});
// Placeholder for user selected PayDate
this.Add(new UserData()
{
NumberOfItems = "ItemAmount Placeholder"
});
}
}
}
This solution does not provide much flexibility currently but it works for the formatting. Marking as resolved to close the ticket

Related

How to bind to IsExpanded property - to persist the TreeView's expanded state?

In the ViewModel I have defined a bool ShouldExpand property, bound to it in from the TreeView's itemcontainerstyle - it does not work.
This answer (WinRT XAML Toolkit TreeView Save state) would've answered my question, but it does not work...
Is there a WORKING code snippet?
public class AgendaItemTreeView : ViewModelBase
{
private string _title;
private Visibility _documentGridVisibility = Visibility.Collapsed;
private bool _shouldExpand;
// Binding property - to display in a TreeView
public string Title
{
get { return _title; }
set { Set(ref _title, value); }
}
// To expand/collapse documents GridView
public Visibility DocumentGridVisibility
{
get { return _documentGridVisibility; }
set { Set(ref _documentGridVisibility, value); }
}
// Property to expand/collapse a node in a TreeView - does not work!
public bool ShouldExpand
{
get { return _shouldExpand; }
set { Set(ref _shouldExpand, value); }
}
// Nested agenda items
public ObservableCollection<AgendaItemTreeView> AgendaItems { get; set; } = new ObservableCollection<AgendaItemTreeView>();
// Documents of current agenda item
public ObservableCollection<DocumentViewModel> Documents { get; set; } = new ObservableCollection<DocumentViewModel>();
public DocumentViewModel SelectedItem { get; set; }
public AgendaItemTreeView(AgendaItem agendaItem, string selectedTitle = "")
{
// Constructor populating the nested AgendaItem & Documents
}
}
XAML:
<UserControl
<UserControl.Resources>
<!--<Style x:Key="TreeViewItemContainerStyle" TargetType="controls:TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding ShouldExpand, Mode=TwoWay}"/>
</Style>-->
<!-- Folder type Node, that can contain other 'folders' and 'files' -->
<DataTemplate x:Key="TreeViewItemTemplate" x:DataType="vm:AgendaItemTreeView">
<data:DataTemplateExtensions.Hierarchy>
<data:HierarchicalDataTemplate ItemsSource="{Binding AgendaItems}" />
<!-- When the next line used together with the Style TreeViewItemContainerStyle - the TreeView still does not expand the top level tree. The app C
<!--<data:HierarchicalDataTemplate ItemsSource="{Binding AgendaItems}" ItemContainerStyle="{StaticResource TreeViewItemContainerStyle}"/>-->
</data:DataTemplateExtensions.Hierarchy>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ToggleButton x:Name="titleToggleBtn" Tapped="{x:Bind ExpandDocumentsCommand}">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{x:Bind DocumentsIcon}" Margin="5,0"/>
<TextBlock Text="{Binding Title}"/>
</StackPanel>
</ToggleButton>
<GridView Grid.Row="1" x:Name="DocumentsGrid" ItemsSource="{Binding Documents}" ItemTemplate="{StaticResource ZoomedInTemplate}" Visibility="{Binding DocumentGridVisibility}" SelectedItem="{Binding SelectedItem}"/>
</Grid>
</DataTemplate>
<!-- Nested 'file' type Node data template -->
<DataTemplate x:Key="ZoomedInTemplate" x:DataType="vm:DocumentViewModel">
<Border BorderBrush="Black" Padding="5" BorderThickness="1" Width="100">
<Grid Tapped="{x:Bind TappedCommand}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<Image Source="{Binding Thumbnail}" Width="60" Height="80" />
<TextBlock Grid.Row="1" Text="{x:Bind Title, Mode=OneWay}" VerticalAlignment="Center" Tapped="{x:Bind TappedCommand}"/>
</Grid>
</Border>
</DataTemplate>
</UserControl.Resources>
<Grid d:DataContext="{d:DesignData /SampleData/AgendaItemTreeViewSampleData.xaml}">
<controls:TreeView x:Name="myTreeView"
ItemsSource="{x:Bind TreeItems}"
ItemTemplate="{StaticResource TreeViewItemTemplate}" />
</Grid>
</UserControl>

XAML Gridview to add an item directly

I successfully coded an XAML Gridview to show list of items.
However, I want to add an [+(ADD ITEM)] button at the end of the Gridview.
The ADD button has should have a custom template (at least a default button) which is different from the content item.
My XAML source is below:
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemsGridView"
AutomationProperties.Name="Items"
TabIndex="1"
Grid.RowSpan="2"
Padding="116,136,116,46"
ItemsSource="{Binding Source={StaticResource DataSource}}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick">
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="250" Height="250">
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
<StackPanel VerticalAlignment="Bottom" Background="{ThemeResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="{Binding Subject}" Foreground="{ThemeResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource BaseTextBlockStyle}" Height="60" Margin="15,0,15,0" FontWeight="SemiBold"/>
<TextBlock Text="{Binding TargetDate}" Foreground="{ThemeResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource BaseTextBlockStyle}" TextWrapping="NoWrap" Margin="15,0,15,10" FontSize="12"/>
</StackPanel>
</Border>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<!--
Something like that..
<Button Content="+" x:Name="btnAdd" />
-->
</GridView>
And, how can I add a ADD button(grid item) to XAML? thanks
You can achieve that by using a DataTemplateSelector, basically what you need to do is :
define another class that will represent the GridView "addItem"
public class GridItem
{
public String Subject { get; set; }
public String TargetDate { get; set; }
}
public class AddGridItem : GridItem
{
public bool IsGridItem { get; set; }
}
Make sure that your GridView's ItemSource collection has always an AddGridItem in it
private ObservableCollection<GridItem> _gridItems =new ObservableCollection<GridItem>()
{
new GridItem()
{
Subject = "Subject1",
TargetDate = "TargetDate"
},new GridItem()
{
Subject = "Subject2",
TargetDate = "TargetDate"
},new GridItem()
{
Subject = "Subject3",
TargetDate = "TargetDate"
},new AddGridItem()
{
IsGridItem = true
}
};
public ObservableCollection<GridItem> GridItems
{
get
{
return _gridItems;
}
set
{
if (_gridItems == value)
{
return;
}
_gridItems = value;
}
}
Create a DataTemplateSelector class that will return the appropriate DataTemplate based on the Item type
public class MyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate RegularTemplate { get; set; }
public DataTemplate AddTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item,
DependencyObject container)
{
if (item is AddGridItem)
return AddTemplate;
if (item is GridItem)
return RegularTemplate;
return base.SelectTemplateCore(item, container);
}
}
Define your dataTempaltes and a DataTemplateSelector as staticresources to use them in your GridView
<Page.Resources>
<DataTemplate x:Key="RegularTemplate">
<Grid HorizontalAlignment="Left" Width="250" Height="250">
<Border BorderThickness="3" BorderBrush="White">
<StackPanel VerticalAlignment="Bottom" Background="LightSkyBlue">
<TextBlock Text="{Binding Subject}" Style="{StaticResource BaseTextBlockStyle}" Height="60" Margin="15,0,15,0" FontWeight="SemiBold"/>
<TextBlock Text="{Binding TargetDate}" Style="{StaticResource BaseTextBlockStyle}" TextWrapping="NoWrap" Margin="15,0,15,10" FontSize="12"/>
</StackPanel>
</Border>
</Grid>
</DataTemplate>
<DataTemplate x:Key="AddItemTemplate">
<Border BorderThickness="3" BorderBrush="White" Background="DodgerBlue">
<FontIcon Glyph="" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White"/>
</Border>
</DataTemplate>
<local:MyDataTemplateSelector x:Key="MyDataTemplateSelector"
RegularTemplate="{StaticResource RegularTemplate}"
AddTemplate="{StaticResource AddItemTemplate}">
</local:MyDataTemplateSelector>
</Page.Resources>
<Grid>
<GridView ItemsSource="{Binding GridItems}" ItemTemplateSelector="{StaticResource MyDataTemplateSelector}">
</GridView>
</Grid>

Update XAML Binding after a property changes

I am using MVVM light on a Windows 8.1 Store app. I have a view model that has several properties on it that have no setters (their value is derived from a different property). Here is part of my ViewModel:
public float TotalAmount
{
get
{
if (!LineItems.Any())
return 0.0f;
return SubTotal + TotalFees - TotalDiscounts;
}
}
public float SubTotal
{
get
{
return !LineItems.Any() ? 0.0f : LineItems.Select(i => i.Price.Amount).Sum();
}
}
public float TotalFees
{
get
{
return !Fees.Any() ? 0.0f : Fees.Select(f => f.Amount).Sum();
}
}
public float TotalDiscounts
{
get
{
return !Discounts.Any() ? 0.0f : Discounts.Select(d => d.Amount).Sum();
}
}
public ObservableCollection<Discount> Discounts { get; set; }
public ObservableCollection<Fee> Fees { get; set; }
public Customer Customer { get; set; }
public ObservableCollection<OrderLineItem> LineItems { get; set; }
In the XAML, I bind to each of these properties, and want to update them when an Item is added to LineItems.
Here is the relevant part of the XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="SubTotal:" Style="{StaticResource OrderDetailsLabel}" />
<TextBlock Grid.Column="1" Style="{StaticResource OrderDetailsValue}"
Text="{Binding SubTotal, Converter={StaticResource MoneyConverter}, Mode=TwoWay}" />
<TextBlock Grid.Row="1" Text="Fees:" Style="{StaticResource OrderDetailsLabel}" />
<TextBlock Grid.Row="1" Style="{StaticResource OrderDetailsValue}" Grid.Column="1"
Text="{Binding TotalFees, Converter={StaticResource MoneyConverter}, Mode=TwoWay}" />
<TextBlock Grid.Row="2" Text="Discounts:" Style="{StaticResource OrderDetailsLabel}" />
<TextBlock Grid.Row="2" Style="{StaticResource OrderDetailsValue}" Grid.Column="1"
Text="{Binding TotalDiscounts, Converter={StaticResource MoneyConverter}, Mode=TwoWay}" />
<TextBlock Grid.Row="3" Text="Order Total:" Style="{StaticResource OrderDetailsLabel}" />
<TextBlock Grid.Row="3" Style="{StaticResource OrderDetailsValue}" Grid.Column="1"
Text="{Binding TotalAmount, Converter={StaticResource MoneyConverter}, Mode=TwoWay}" />
</Grid>
My Binding to the LineItems is working as expected, but the fields that are populated by doing math on the line items are not.
Line Item Binding functioning as expected:
<ListView ItemsSource="{Binding LineItems, Mode=TwoWay}"
SelectionMode="Single"
HorizontalAlignment="Stretch">
<ListView.ItemContainerStyle>
....
How can I get SubTotal, Total, etc. to update when LineItems changes?
Pretty simple:
Register the ObservableCollection<T>.CollectionChanged event for each of those collection. Since your desired properties are based on some calculation of the collection.
On event fire Raise the OnPropertyChanged on those properties.
Example how to raise the OnPropertyChanged/inotifypropertychanged
MSDN: inotifypropertychanged Example
Something like this in your constructor:
LineItems.CollectionChanged += (s, e) =>
{
RaisePropertyChanged("SubTotal");
RaisePropertyChanged("TotalFees");
RaisePropertyChanged("TotalDiscounts");
}
assuming your ViewModel implements INotifyPropertyChanged, and has a 'RaisePropertyChanged' method that raises the PropertyChanged event (e.g. You're using MVVM-Light's ViewModelBase as your baseclass).
I ended up solving this by raising the OnPropertyChanged event on in the handler for my AddLineItems message:
public NewOrderViewModel()
{
//Subscribe to messages
MessengerInstance.Register<OrderItemAdded>(this, o => AddOrderItem(o.LineItem));
MessengerInstance.Register<SelectedCustomer>(this, c => Customer = c.Customer);
Discounts = new ObservableCollection<Discount>();
Fees = new ObservableCollection<Fee>();
LineItems = new ObservableCollection<OrderLineItem>();
}
private void AddOrderItem(OrderLineItem lineItem)
{
//add Line Item to collection
LineItems.Add(lineItem);
//Raise events for properties derived from collection
RaisePropertyChanged("TotalAmount");
RaisePropertyChanged("SubTotal");
}

WP8 xaml causes "element is already child of another element"

I am following MSDN examples to add a settings page to my first Windows Phone 8 application (warning - I am completely new to XAML, I'm a C++ guy).
The xaml looks like this:
<phone:PhoneApplicationPage
x:Class="PicoSDU.AppSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PicoSDU"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True">
<phone:PhoneApplicationPage.Resources>
<local:AppSettings x:Key="PicoSettings"></local:AppSettings>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="PicoSDU" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="Settings" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel Margin="30,0,0,0">
<TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtIpAddress" Text="IP Address" VerticalAlignment="Top" Width="169" />
<TextBox Name="tbIpAddress" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"
Text="{Binding Source={StaticResource PicoSettings}, Path=IpSetting, Mode=TwoWay}"/>
<TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtPort" Text="Port Number" VerticalAlignment="Top" Width="169" />
<TextBox Name="tbPort" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"
Text="{Binding Source={StaticResource PicoSettings}, Path=PortSetting, Mode=TwoWay}"/>
<TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtSysId" Text="System ID" VerticalAlignment="Top" Width="169" />
<TextBox Name="tbSysId" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"
Text="{Binding Source={StaticResource PicoSettings}, Path=SysIdSetting, Mode=TwoWay}"/>
<TextBlock Height="36" HorizontalAlignment="Left" Margin="0,0,0,0" Name="txtWsId" Text="Station ID" VerticalAlignment="Top" Width="169" />
<TextBox Name="tbWsId" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="274"
Text="{Binding Source={StaticResource PicoSettings}, Path=WsIdSetting, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
So, pretty simple. four text boxes. It rendered perfectly OK until I added the resource clause
<phone:PhoneApplicationPage.Resources>
<local:AppSettings x:Key="PicoSettings"></local:AppSettings>
</phone:PhoneApplicationPage.Resources>
As soon as I add that the XAML parser throws a wobbler and the root PhoneApplicationPage gets the old blue squiggly and reports our favourite "element is already the child of another element" error. If I remove the resource clause, that error goes away and the xaml renders, but of course the textbox bindings then all throw an error because they cannot see their resources.
I've been googling this the last three hours and I can't see what's wrong, and none of the answers I've found here and elsewhere seem to fit. Can some kind soul show me the blindingly stupid thing I've done and please put me out of my misery?
Edit
Here's the AppSettings class. It's just the Microsoft code sample, hacked into the code-behind:
namespace PicoSDU
{
public partial class AppSettings : PhoneApplicationPage
{
// Our settings
IsolatedStorageSettings settings;
// The key names of our settings
const string IpSettingKeyName = "IpSetting";
const string SysIdSettingKeyName = "SysIdSetting";
const string WsIdSettingKeyName = "WsIdSetting";
const string PortSettingKeyName = "PortSetting";
// The default value of our settings
const string IpSettingDefault = "81.179.24.51";
const string SysIdSettingDefault = "1";
const string WsIdSettingDefault = "511";
const string PortSettingDefault = "1846";
public AppSettings()
{
InitializeComponent ();
try
{
settings = IsolatedStorageSettings.ApplicationSettings;
}
catch (System.IO.IsolatedStorage.IsolatedStorageException e)
{
// handle exception
}
}
public bool AddOrUpdateValue(string Key, Object value)
{
bool valueChanged = false;
// If the key exists
if (settings.Contains(Key))
{
// If the value has changed
if (settings[Key] != value)
{
// Store the new value
settings[Key] = value;
valueChanged = true;
}
}
// Otherwise create the key.
else
{
settings.Add(Key, value);
valueChanged = true;
}
return valueChanged;
}
public T GetValueOrDefault<T>(string Key, T defaultValue)
{
T value;
// If the key exists, retrieve the value.
if (settings.Contains(Key))
{
value = (T)settings[Key];
}
// Otherwise, use the default value.
else
{
value = defaultValue;
}
return value;
}
public void Save()
{
settings.Save();
}
public string IpSetting
{
get
{
return GetValueOrDefault<string>(IpSettingKeyName, IpSettingDefault);
}
set
{
if (AddOrUpdateValue(IpSettingKeyName, value))
{
Save();
}
}
}
public string SysIdSetting
{
get
{
return GetValueOrDefault<string> ( SysIdSettingKeyName, SysIdSettingDefault );
}
set
{
if (AddOrUpdateValue ( SysIdSettingKeyName, value ))
{
Save ();
}
}
}
public string WsIdSetting
{
get
{
return GetValueOrDefault<string> ( WsIdSettingKeyName, WsIdSettingDefault );
}
set
{
if (AddOrUpdateValue ( WsIdSettingKeyName, value ))
{
Save ();
}
}
}
public string PortSetting
{
get
{
return GetValueOrDefault<string> ( PortSettingKeyName, PortSettingDefault );
}
set
{
if (AddOrUpdateValue ( PortSettingKeyName, value ))
{
Save();
}
}
}
}
}
Your code is quite bizzare. You are trying to embed one Page (your AppSettings class inherits from PhoneApplicationPage) into another. A much better approach would be to use the MVVM pattern.
Do not make the AppSettings inherit from PhoneApplicationPage and make it into a ViewModel. More info at http://msdn.microsoft.com/en-us/library/windowsphone/develop/gg521153(v=vs.105).aspx

No data with ListBox with Grid inside

I am doing a Grid of two columns that are inside a ListBox. That after that I can DataBind the two columns to be repeated vertically.
So far the code below shows nothing on the WP7 emulator.
<ListBox Background="Yellow" ItemsSource="{Binding}" Height="100" Margin="0,0,8,0">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="100">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<TextBlock Text="Channels" HorizontalAlignment="Stretch" Foreground="Black" Grid.Column="0" />
<TextBlock Text="Antenna" HorizontalAlignment="Stretch" Foreground="Black" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Please help me.
If your only concern is that you see ItemTemplate in action, you can supply explicit non-UI items as follows:
<ListBox Background="Yellow" Height="100" Margin="0,0,8,0" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="30">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<TextBlock Text="Channels" HorizontalAlignment="Stretch" Foreground="Black" Grid.Column="0" />
<TextBlock Text="Antenna" HorizontalAlignment="Stretch" Foreground="Black" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<sys:String>1111111</sys:String>
<sys:String>2222222</sys:String>
<sys:String>3333333</sys:String>
</ListBox>
Notes:
I removed ItemsSource and supplied items explicitly.
Items must not derive from UIElement so that they are templated. (UIElements are simply drawn and the template is ignored.)
I added System namespace so that string objects can be specified.
I decreased ItemTemplate height so that more than one list row is visible.
Easier solution:
Give the ListBox a name and remove the binding:
<ListBox x:Name="myLB" Background="Yellow" Height="100" Margin="0,0,8,0">
Then use this line in the code (after the call InitializeComponent()):
myLB.ItemsSource = new List<string> { "First", "Second", "Third" };
If you want design-time itemssource, you can use the IsInDesignMode property like so:
if (System.ComponentModel.DesignerProperties.IsInDesignTool)
{
myListBox.ItemsSource = GenerateMockItems();
}
else
{
myListBox.ItemsSource = GetRealItems();
}
in MVVMLight ViewModels, this is shortcut-ed as
if (IsInDesignMode)
{
}
Similarly, since it looks like you're setting your ItemsSource in xaml, inside your class that is your DataContext, you could do something like
public class MyViewModel
{
public MyViewModel()
{
if (System.ComponentModel.DesignerProperties.IsInDesignTool)
{
Items = GenerateMockItems();
EditTime = GenerateRandomFutureDate();
}
else
{
//whatever you expect should happen in runtime
}
}
//what list is binding to
public ObservableCollection<Item> Items { get; set; }
//other properties.. for example
public bool HasItems { get { return Items != null && Items.Count > 0; } }
public DateTime EditDate { get; set; }
private ObservableCollection<Item> GenerateMockItems()
{
var collection = new ObservableCollection<Item>();
for (int i = 0; i < 10; i++)
{
collection.Add(new Item() { Name="sdfsdfs" , Channel=i });
}
return collection;
}
private DateTime GenerateRandomFutureDate()
{
return DateTime.Now.AddSeconds(new Random().Next(0,50000));
}
}