To start off, I have a listbox that is trying to accept a UserControl as the DataTemplate:
<ListBox VerticalAlignment="Stretch" Name="GeneralMcmView" Grid.Column="0" HorizontalAlignment="Stretch" >
<ListBox.ItemTemplate>
<DataTemplate DataType="local:GeneralMcmMessage">
<local:GeneralMcmMessage />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
With the contents of the usercontrol looking like:
<ContentControl FontFamily="Segoe UI" VerticalAlignment="Stretch" FontSize="10">
<Grid VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" Grid.Row="0">
<TextBlock Name="MessageDateTime" Text="{Binding ElementName=_this, Path=TimeStamp, StringFormat=MM/dd/yyyy h:mm:ss.fff tt \'GMT\' (zzz)}" />
<TextBlock Name="MessageTypeLabel" Margin="15,0,5,0" Text="Type:"/>
<TextBlock Name="MessageType" Text="{Binding ElementName=_this, Path=Type}" />
</StackPanel>
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" Grid.Row="1">
<TextBlock Name="MessageNameLabel" Margin="0,0,5,0" Text="Message Name:" />
<TextBlock Name="MessageNameValue" Text="{Binding ElementName=_this, Path=MessageName}" TextWrapping="Wrap" />
</StackPanel>
<StackPanel VerticalAlignment="Stretch" Orientation="Vertical" Grid.Row="2">
<TextBlock Name="MessageLabel" Text="Message:"/>
<TextBlock Name="Message" Margin="10,0,0,0" Text="{Binding ElementName=_this, Path=MessageContent}" />
</StackPanel>
</Grid>
</ContentControl>
I then create a couple messages, all with different data (The Listbox's ItemSource is bound to the GeneralMessages ObservableCollection):
GeneralMcmMessage newMsg = new GeneralMcmMessage()
{
MessageId = e.McmMessageViewInfo.Id,
TimeStamp = e.McmMessageViewInfo.MessageDateTime,
Type = e.McmMessageViewInfo.MessageType.ToString(),
MessageName = e.McmMessageViewInfo.MessageName,
MessageContent = e.McmMessageViewInfo.Message.ToString()
};
GeneralMessages.Add( newMsg );
During runtime I interrogate the Items property of the listbox and all the data looks correct, however all I see in the listbox are entries of the GeneralMcmMessage User Control with default data values. Any ideas as to why?
Also, FWIW I am using INotifyPropertyChanged in the usercontrol class:
public partial class GeneralMcmMessage : UserControl, INotifyPropertyChanged
{
private Constants.PiuModule piuModule = Constants.PiuModule.MCM;
private string className = "GeneralMcmMessage";
/// <summary>
/// Event for notifying listeners that a property changed. Part of INotifyPropertyChanged
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
public int MessageId { get; set; }
private DateTime timeStamp;
public DateTime TimeStamp
{
get
{
return timeStamp;
}
set
{
timeStamp = value;
OnNotifyPropertyChanged( "TimeStamp" );
}
}
You're saying that the DataTemplate to display a GeneralMcmMessage is to instantiate new GeneralMcmMessage.
<DataTemplate DataType="local:GeneralMcmMessage">
<local:GeneralMcmMessage />
</DataTemplate>
I'd recommend instead of making a collection of your UserControl to make a collection of your model object instead, and bind to the properties within that within your UserControl.
Either way though - the object you created in code will be the DataContext for the one you created in XAML so removing the ElementName=_this should bind appropriately. Try this simplified XAML in your UserControl instead.
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" Grid.Row="0">
<TextBlock Name="MessageDateTime" Text="{Binding TimeStamp, StringFormat=MM/dd/yyyy h:mm:ss.fff tt \'GMT\' (zzz)}" />
<TextBlock Name="MessageTypeLabel" Margin="15,0,5,0" Text="Type:"/>
<TextBlock Name="MessageType" Text="{Binding Type}" />
</StackPanel>
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" Grid.Row="1">
<TextBlock Name="MessageNameLabel" Margin="0,0,5,0" Text="Message Name:" />
<TextBlock Name="MessageNameValue" Text="{Binding MessageName}" TextWrapping="Wrap" />
</StackPanel>
<StackPanel VerticalAlignment="Stretch" Orientation="Vertical" Grid.Row="2">
<TextBlock Name="MessageLabel" Text="Message:"/>
<TextBlock Name="Message" Margin="10,0,0,0" Text="{Binding MessageContent}" />
</StackPanel>
You don’t post all code of GeneralMcmMessage hence I don’t know if you set DataContext in user control for example in constructor of GeneralMcmMessage.
I tried replicate your problem.
User control GeneralMcmMessage code behind
public partial class GeneralMcmMessage : UserControl, INotifyPropertyChanged
{
private int _messageId;
public int MessageId
{
get
{
return _messageId;
}
set
{
_messageId = value;
OnPropertyChanged("MessageId");
}
}
private DateTime _timeStamp;
public DateTime TimeStamp
{
get
{
return _timeStamp;
}
set
{
_timeStamp = value;
OnPropertyChanged("TimeStamp");
}
}
public GeneralMcmMessage()
{
InitializeComponent();
//don’t set data context here
//DataContext = this;
}
}
User control GeneralMcmMessage XAML
<StackPanel>
<TextBlock Margin="5" FontSize="20" Text="{Binding Path=MessageId}"/>
<TextBlock Margin="5" FontSize="20" Text="{Binding Path=TimeStamp}"/>
</StackPanel>
User control GeneralMcmMessage usage
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<GeneralMcmMessage> _generalMessages;
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public ObservableCollection<GeneralMcmMessage> GeneralMcmMessages
{
get { return _generalMessages; }
set
{
_generalMessages = value;
OnPropertyChanged("GeneralMcmMessages");
}
}
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
GeneralMcmMessages = new ObservableCollection<GeneralMcmMessage>();
for (int i = 0; i < 10; i++)
{
var newMsg = new GeneralMcmMessage
{
MessageId = i,
TimeStamp = DateTime.Now,
};
GeneralMcmMessages.Add(newMsg);
}
}
}
User control GeneralMcmMessage usage XAML
<ListBox x:Name="ListBox"
Margin="5"
ItemsSource="{Binding Path=GeneralMcmMessages}">
<ListBox.ItemTemplate>
<DataTemplate DataType="stackoverflow:GeneralMcmMessage">
<stackoverflow:GeneralMcmMessage/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If this not solve your problem could you please post all GeneralMcmMessage?
If you want I can upload sample project for you.
Thank you
Related
I have a quite complicated structure of ListViews. Inside this structure are TextBoxes with values from my ViewModel. When I change a value in some textbox, the property in ViewModel doesn't update. The "AllTexts" property in ViewModel still contains only "Hello" strings.
Basically, I want to show user structure of strings and then let the user change this structure. After he finishes his modification I want to save his changes. "Hello" strings are here just for testing.
My ViewModel:
class MainWindowViewModel
{
public ObservableCollection<ObservableCollection<ObservableCollection<string>>> AllTexts { get; set; }
public int SelectedGroupIndex { get; set; }
public int SelectedColumnIndex { get; set; }
public ICommand AddGroup { get; private set; }
public ICommand AddColumn { get; private set; }
public MainWindowViewModel()
{
this.AllTexts = new ObservableCollection<ObservableCollection<ObservableCollection<string>>>();
this.SelectedGroupIndex = -1;
this.SelectedColumnIndex = -1;
this.AddGroup = new Command(this.AddGroupCommandHandler);
this.AddColumn = new Command(this.AddColumnCommandHandler);
}
private void AddGroupCommandHandler()
{
var tempColumn = new ObservableCollection<string>() { "Hello", "Hello", "Hello", "Hello", "Hello" };
var tempGroup = new ObservableCollection<ObservableCollection<string>>();
tempGroup.Add(tempColumn);
this.AllTexts.Add(new ObservableCollection<ObservableCollection<string>>(tempGroup));
}
private void AddColumnCommandHandler()
{
if (this.SelectedGroupIndex >= 0 && this.SelectedGroupIndex < this.AllTexts.Count)
{
var tempColumn = new ObservableCollection<string>() { "Hello", "Hello", "Hello", "Hello", "Hello" };
this.AllTexts[this.SelectedGroupIndex].Add(tempColumn);
}
}
}
My View:
<Window.Resources>
<ResourceDictionary>
<local:MainWindowViewModel x:Key="vm" />
</ResourceDictionary>
</Window.Resources>
<Grid Margin="10,10,10,10" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="300" />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ListView Grid.Row="0"
ItemsSource="{Binding AllTexts, Source={StaticResource vm}, Mode=TwoWay}"
Background="Red"
SelectedIndex="{Binding SelectedGroupIndex, Source={StaticResource vm}}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<ListView
Background="Yellow"
ItemsSource="{Binding Path=., Mode=TwoWay}"
SelectedIndex="{Binding SelectedColumnIndex, Source={StaticResource vm}}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<ListView
Background="Green"
ItemsSource="{Binding Path=., Mode=TwoWay}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=., Mode=TwoWay, NotifyOnSourceUpdated=True}"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"
Width="100" Height="40"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,20,0,0">
<Button Content="Add Group" Width="120" Height="30"
Command="{Binding AddGroup, Source={StaticResource vm}}" />
<Button Content="Add Column" Margin="20,0,0,0" Width="120" Height="30"
Command="{Binding AddColumn, Source={StaticResource vm}}" />
</StackPanel>
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,20,0,0">
<TextBlock Width="120" Height="30" FontSize="20"
Text="{Binding SelectedGroupIndex, Source={StaticResource vm}}" />
<TextBlock Width="120" Height="30" Margin="20,0,0,0" FontSize="20"
Text="{Binding SelectedColumnIndex, Source={StaticResource vm}}" />
</StackPanel>
</Grid>
Could someone, please help me?
Thank you.
Your ViewModel has to notify the View about the changes, or else the View retains original values of the ViewModel
In this case, string cannot notify the changes made to itself. Only its enclosing observable collection can notify about changes made to itself like add or remove and does not monitor further into its elements.
So you need an observable string:
public class MyString : DependencyObject
{
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(MyString), new PropertyMetadata(""));
}
To use in the collection:
public ObservableCollection<ObservableCollection<ObservableCollection<MyString>>> AllTexts { get; set; }
I also added the following line to the MyString class in order to test the code and it worked.
public static MyString Hello { get { return new MyString { Value = "Hello" }; } }
Obviously, this is how it will be used:
var tempColumn = new ObservableCollection<MyString>() { MyString.Hello, MyString.Hello, MyString.Hello, MyString.Hello, MyString.Hello };
In xaml there are also some unnecessary things which you can get rid of:
use ItemsSource="{Binding}" for both ListViews, and use Text="{Binding Value}" for the TextBox. (there is no need for explicit TwoWay in any of those)
Issue
I have a number of buttons in my WPF Window which when clicked need to change the view on the Window but keep the same ViewModel. Yesterday I tried using ControlTemplate for this but people mentioned I was better using a DataTemplate.
I need the binding to happen Via the ViewModel as well as I need to do some checks to see if the user can access the view.
Code
This is some of the code i started to write but I feel like its incorrect.
Here is the DataTemplate that I have defined in my view in Window.Resources:
<DataTemplate x:Key="panel1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="7*"/>
<ColumnDefinition Width="110*"/>
<ColumnDefinition Width="190*"/>
<ColumnDefinition Width="110*"/>
<ColumnDefinition Width="202*"/>
<ColumnDefinition Width="109*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="74*"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="12*"/>
<RowDefinition Height="39*"/>
<RowDefinition Height="11*"/>
<RowDefinition Height="38*"/>
<RowDefinition Height="5*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Column="2" Grid.ColumnSpan="3" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label Content="Video Set:" Foreground="#e37e6e" Grid.Column="2" Grid.ColumnSpan="3" VerticalAlignment="Center" FontSize="22" HorizontalAlignment="Center"/>
<Image Source="{Binding VideoSet}" Height="25" Width="25" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
<TextBlock Foreground="#e37e6e" FontSize="12" Text="You currently do not have a video set. Please click the button below to add a video. Please note you will not be able to create an On Demand presentation without a media file selected. " Grid.Row="1" Grid.Column="2" TextWrapping="WrapWithOverflow" TextAlignment="Center" Grid.ColumnSpan="3" />
<Button Style="{StaticResource loginButton}" Command="{Binding ScreenBack}" Foreground="White" Content="Add Video" Grid.Column="3" HorizontalAlignment="Stretch" Grid.Row="3" VerticalAlignment="Stretch" Grid.ColumnSpan="1"></Button>
</Grid>
</DataTemplate>
Then I tried to use a ContentPresenter and bind to the DataTemplate:
<ContentPresenter Content="{Binding}" Grid.Row="3" Grid.RowSpan="1" Grid.ColumnSpan="5"/>
Now I want to be able to bind different DataTemplates to the ContentPresenter Via the ViewModel, can anyone help me with this issue?
EDIT:
I can bind the ContentPresenter to the DataTemplate through the static resource like below:
<ContentPresenter ContentTemplate="{StaticResource panel1}" Content="{StaticResource panel1}" Grid.Row="3" Grid.RowSpan="1" Grid.ColumnSpan="5"/>
The DataTemplate like below:
<DataTemplate x:Key="panel1">
</DataTemplate>
But how can i change the ControlPresenter binding from the ViewModel?
EDIT:
Here is my code cycle:
So here are the two DataTemplates:
<DataTemplate DataType="{x:Type local:ViewModelA}">
<TextBlock Foreground="#e37e6e" FontSize="12" Text="You currently do not have a video set. Please click the button below to add a video. Please note you will not be able to create an On Demand presentation without a media file selected. " Grid.Row="1" Grid.Column="2" TextWrapping="WrapWithOverflow" TextAlignment="Center" Grid.ColumnSpan="3" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelB}">
<TextBlock Foreground="#e37e6e" FontSize="12" Text="NEWWWWWWWWWWYou" Grid.Row="1" Grid.Column="2" TextWrapping="WrapWithOverflow" TextAlignment="Center" Grid.ColumnSpan="3" />
</DataTemplate>
The my ContentControl:
<ContentControl Content="{Binding SelectedViewModel}" />
I defined my DataContext in the code behind:
WizardViewModel _wizardViewModel = new WizardViewModel();
this.DataContext = _wizardViewModel;
In the WizardViewModel i have:
namespace Podia2016.ViewModels
{
public class WizardViewModel : INotifyPropertyChanged
{
public object SelectedViewModel { get; set; }
ViewModelA s = new ViewModelA();
ViewModelB d = new ViewModelB();
public WizardViewModel()
{
SelectedViewModel = s;
OnPropertyChanged("SelectedViewModel");
}
//BC - BINDS TO CHANGE LECTURE.
public ICommand Next
{
get { return new DelegateCommand<object>(Next_Click); }
}
private void Next_Click(object obj)
{
SelectedViewModel = d;
OnPropertyChanged("SelectedViewModel");
}
}
public class ViewModelA : INotifyPropertyChanged
{
//BC - DEFAULT ONPROPERTYCHANGED EVENT.
public event PropertyChangedEventHandler PropertyChanged;
public ViewModelA()
{
}
/// <summary>
/// This is the standard OnPropertyChanged Event Method
/// </summary>
/// <param name="name"></param>
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public class ViewModelB : INotifyPropertyChanged
{
//BC - DEFAULT ONPROPERTYCHANGED EVENT.
public event PropertyChangedEventHandler PropertyChanged;
public ViewModelB()
{
}
/// <summary>
/// This is the standard OnPropertyChanged Event Method
/// </summary>
/// <param name="name"></param>
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
Data templating is much simpler to use (compared to your attempt):
create sub viewmodels, one for each sub view you want to have
// could have base class, may have to implement INotifyPropertyChanged, etc.
public class ViewModelA { }
public class ViewModelB
{
public string SomeProperty { get; }
}
...
public object SelectedViewModel { get; set; }
define data templates
<SomeContainer.Resources>
<!-- using user control -->
<DataTemplate DataType="{x:Type local:ViewModelA}">
<local:UserControlA />
</DataTemplate>
<!-- or like this -->
<DataTemplate DataType="{x:Type local:ViewModelB}">
<Grid>
<TextBlock Text="{Binding SomeProperty}" />
<Button .../>
...
</Grid>
</DataTemplate>
Bind content control to a property which select sub viewmodel
<ContentControl Content="{Binding SelectedViewModel}" />
control what to display by changing selected sub viewmodel:
var a = new ViewModelA();
var b = new ViewModelB() { SomeProperty = "Test" };
// display a - will display UserControlA content in ContentControl
SelectedViewModel = a;
OnPropertyChanged(nameof(SelectedViewModel));
// display b - will display text and button in ContentControl
SelectedViewModel = b;
OnPropertyChanged(nameof(SelectedViewModel));
Store DataTemplate as property of your ViewModel. Access the DataTemplate from ResourceDictionary to store in your property.
Bind <ContentPresenter Content="{Binding}" ContentTemplate="{Binding template1}" .../>
How to access ResourceDictionary from code :
If you have in your WPF project an ResourceDictionary that you use to define resources you can create an instance of it from code like this:
ResourceDictionary res = Application.LoadComponent(
new Uri("/WpfApplication1;component/MyDataTemplateCollection.xaml",
UriKind.RelativeOrAbsolute)) as ResourceDictionary;
Where WpfApplication1 is name of your assembly and MyDataTemplateCollection.xaml is name of your ResourceDictionary.
Another way is to use the code-behind for the resource dictionary.
Add x:Class to your ResourceDictionary:
Add class MyDataTemplateCollection.xaml.cs as your code-behind for the ResourceDictionary.
The code behind class looks like so:
partial class MyDataTemplateCollection: ResourceDictionary
{
public MyDataTemplateCollection()
{
InitializeComponent();
}
}
Usage :
ResourceDictionary res = new MyDataTemplateCollection();
I wrote user control with 2 buttons and one check box and now I want to bind Commands to data context - for each button and checkbox.
But I don't know how to define command binding. I think I'll need some kind of ICommand property in User control - but how can I connect user's data context command delegate? I want to use user control to manage each item in collection like this:
<ItemsControl ItemsSource="{Binding Path=MoneyInfo}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ChannelSetupControl
CurrentCount="{Binding Count}"
CoinValue="{Binding Value}"
UpCommand="{Binding DataContextUp}"
DownCommand="{Binding DataContextDown}"
ChangeCheckboxCommand="{Binding DataContextChange}"></local:ChannelSetupControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
XAML User control
<UserControl>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="3*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Text="{Binding CoinValue}" TextAlignment="Center"></TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1" Text="{Binding CurrentCount, Mode=TwoWay}" TextAlignment="Center" VerticalAlignment="Center" FontSize="30"></TextBlock>
<StackPanel Grid.Column="1" Grid.Row="1" VerticalAlignment="Center">
<Button Content="+ 10" Padding="0 5"></Button>
<Button Content="- 10" Padding="0 5"></Button>
</StackPanel>
<CheckBox Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" IsChecked="{Binding Cycling, Mode=TwoWay}" Content="recycling" VerticalContentAlignment="Center"></CheckBox>
</Grid>
</UserControl>
and code behind and this is where I'm lost - how to define UpCommand, DownCommand and ChangeCheckboxCommand?
public partial class ChannelSetupControl : UserControl, INotifyPropertyChanged
{
private int currentCount;
private bool cycling;
private double coinValue;
public int Step { get; set; }
public double CoinValue { get { return coinValue; } set { coinValue = value; NotifyPropertyChanged("CoinValue"); } }
public int CurrentCount { get { return currentCount; } set { currentCount = value; NotifyPropertyChanged("CurrentCount"); } }
public bool Cycling { get { return cycling; } set { cycling = value; NotifyPropertyChanged("Cycling"); } }
public ChannelSetupControl()
{
InitializeComponent();
DataContext = this;
CurrentCount = 0;
Step = 10;
Cycling = false;
CoinValue = 0;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
First of all your ChannelSetupControl class extends UserControl, so it implicitly extends DependencyObject class. It means you can use Dependency Properties instead of implementing INotifyPropertyChanged.
So you can define a dependency property in your ChannelSetupControl class, like this one:
public static readonly DependencyProperty UpCommandProperty =
DependencyProperty.Register("UpCommand", typeof(ICommand), typeof(ChannelSetupControl));
public ICommand UpCommand
{
get { return (ICommand)GetValue(UpCommandProperty); }
set { SetValue(UpCommandProperty, value); }
}
At the same time in your control XAML:
<Button Command="{Binding RelativeSource={RelativeSource Mode=Self}, Path=UpCommand, Mode=OneWay}"
Content="+ 10" Padding="0 5" />
In this way in your window XAML you can wrote:
<local:ChannelSetupControl UpCommand="{Binding UpCommand, Mode=OneWay}" ... />
You can use the same "pattern" for the other controls.
Regarding ICommand, there are a lot of implementations. The one that I prefer is the so called delegate command (for a sample you can take a look here).
I hope this quick explanation can help you.
First time using a ListBox and after following this , I'm having issues having data actually display. The ListBox is just empty and white with no text in it.
I made a separate textbox to test an individual "Tweet" object out and it is indeed outputting what I want it to. I think my issue either lies in XAML or Tweets. But nothing looks out of place.
Tracing reveals that Tweets successfully adds a proper Tweet object with what I need. But my ListBox Count is always 0.
<Grid Opacity="0.8">
<Grid.Resources>
<local:Tweets x:Key="tweets"/>
</Grid.Resources>
<Rectangle Fill="Gray" Margin="1523,0,0,729" Height="321" Width="389">
<Rectangle.Effect>
<DropShadowEffect/>
</Rectangle.Effect></Rectangle>
<ListBox ItemsSource="{StaticResource tweets}" Height="321" Margin="340,40,1096,0" x:Name="twitterBox" VerticalAlignment="Top" Width="476">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="132">
<!--<Image Source="{Binding imgSrc}" Height="73" Width="73" VerticalAlignment="Top" Margin="0,10,8,0"/>-->
<StackPanel Width="370">
<TextBlock Text="{Binding user}" FontSize="28" />
<TextBlock Text="{Binding tweet}" TextWrapping="Wrap" FontSize="24" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
In my cs:
public class Tweet
{
public String imgSrc { get; set; }
public String user { get; set; }
public String tweet { get; set; }
public Tweet(String user, String tweet, String img)
{
this.imgSrc = img;
this.user = user;
this.tweet = tweet;
}
}
public class Tweets : ObservableCollection<Tweet>
{
public Tweets()
{
}
public void addTweet(Tweet tweet)
{
Add(tweet);
}
}
public void SomeFunction()
{
Tweets myTwitter = new Tweets();
myTwitter.addTweet(new Tweet(tweet.User.ScreenName, tweet.Text, tweet.User.ProfileImageUrl));
}
ItemTemplate code is ok but you should remove this Margin="1523,0,0,729".
ListBox is empty because items source is empty. You should add some items.
To add items in XAML you should add default constructor to Tweet class.
public class Tweet
{
public String imgSrc { get; set; }
public String user { get; set; }
public String tweet { get; set; }
public Tweet(){}
public Tweet(String user, String tweet, String img)
{
this.imgSrc = img;
this.user = user;
this.tweet = tweet;
}
}
And now you can write something like this:
...
<Grid.Resources>
<local:Tweets x:Key="tweets">
<local:Tweet imgSrc="imgSrc1" user="user1" tweet="tweet1" />
<local:Tweet imgSrc="imgSrc2" user="user2" tweet="tweet2" />
</local:Tweets>
</Grid.Resources>
...
Result:
Add items in code-behind.
To do that you should use function: FindResource (msdn).
XAML:
<Grid Name="mainGrid" Opacity="0.8">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.Resources>
<local:Tweets x:Key="tweets">
<local:Tweet imgSrc="imgSrc1" user="user1" tweet="tweet1" />
<local:Tweet imgSrc="imgSrc2" user="user2" tweet="tweet2" />
</local:Tweets>
</Grid.Resources>
<Button Content="Add new item" Click="Button_Click" />
<ListBox x:Name="twitterBox" ItemsSource="{StaticResource tweets}"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="132">
<!--<Image Source="{Binding imgSrc}" Height="73" Width="73" VerticalAlignment="Top" Margin="0,10,8,0"/>-->
<StackPanel Width="370">
<TextBlock Text="{Binding user}" FontSize="28" />
<TextBlock Text="{Binding tweet}" TextWrapping="Wrap" FontSize="24" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Code-behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
var coll = mainGrid.FindResource("tweets") as Tweets;
if (coll != null)
{
coll.Add(new Tweet("user", "name", "url"));
}
}
Second solution:
The better solution will be if you will create an instance of class Tweets in code behind.
XAML:
<Grid Name="mainGrid" Opacity="0.8">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Content="Add new item" Click="Button_Click" />
<ListBox x:Name="twitterBox" ItemsSource="{Binding tweets}"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="132">
<!--<Image Source="{Binding imgSrc}" Height="73" Width="73" VerticalAlignment="Top" Margin="0,10,8,0"/>-->
<StackPanel Width="370">
<TextBlock Text="{Binding user}" FontSize="28" />
<TextBlock Text="{Binding tweet}" TextWrapping="Wrap" FontSize="24" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
tweets = new Tweets();
tweets.Add(new Tweet("user1", "name1", "url1"));
tweets.Add(new Tweet("user2", "name2", "url2"));
tweets.Add(new Tweet("user3", "name3", "url3"));
this.DataContext = this;
}
public Tweets tweets { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
tweets.Add(new Tweet("user4", "name4", "url4"));
}
}
You add tweets to a different collection than the one displayed by the listbox.
<ListBox Height="434" HorizontalAlignment="Left" Margin="6,238,0,0" Name="listBox1" VerticalAlignment="Top" Width="432" DataContext="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" Margin="4" Foreground="{StaticResource PhoneAccentBrush}"></TextBlock>
<StackPanel Orientation="Horizontal" Margin="4">
<TextBlock Text="Set" Margin="16" Foreground="{StaticResource PhoneAccentBrush}" />
<TextBlock Text="Weight" Margin="16" Foreground="{StaticResource PhoneAccentBrush}" />
<TextBlock Text="Reps" Margin="10,16,0,16" Foreground="{StaticResource PhoneAccentBrush}" />
</StackPanel>
<ListBox Name="setsAndReps" Height="auto" Width="auto" ItemsSource="{Binding Sets}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding SetNumber}"/>
<TextBox Text="{Binding Weight}"/>
<TextBox Text="{Binding Reps}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The outer listbox's item source is being set to an observable collection of a user defined class called excercise
public class excercise : IComparable, IEquatable<excercise>, INotifyPropertyChanged
{
string name;
int max;
int NUM_SETS;
ObservableCollection<set> sets;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public excercise(string name)
{
this.name = name;
this.NUM_SETS = 0;
this.sets = new ObservableCollection<set>();
}
public ObservableCollection<set> Sets
{
get{return this.sets; }
}
public ObservableCollection<set> getSets()
{
return this.sets;
}
}
The properties in the inner list box are from the set class but none of them are being displayed and I am not sure what the problem is.
your first listbox doesn't have any itemsource set and the datacontext is set to an empty binding (both on line 1 of your code)