Cannot apply Click event to the button with the custom button style - c#

Following is the structure of my data:
public class TokenItems
{
public int ID { get; set; }
public int itemID { get; set; }
public string name { get; set; }
public int qty { get; set; }
public int twQty { get; set; }
public bool readyStatus { get; set; }
public Nullable<DateTime> orderOn { get; set; }
public int styleType { get; set; }
}
public class Token
{
public int tokenNo { get; set; }
public Nullable<DateTime> startedOn { get; set; }
public List<TokenItems> tokenItems { get; set; }
public bool readyStatus { get; set; }
public bool acceptStatus { get; set; }
}
The above structure fits well in the following DataTemplate. (It has multiple data template, DataTemplate within the DataTemplate)
TokenPanel has the data from Token class which is assigned from code behind like this:
TokenPanel.ItemsSource = List<Token> filledList;
tokenItems is assigned within the XAML:
ItemsSource = {Binding List<TokenItems> tokenItem}
The Template of tokenItems further contains the template of buttons which are created from the list items of the tokenItem.
I have applied custom style (redButton) to the button with Click event (listClick).
<ScrollViewer>
<ItemsControl x:Name="TokenPanel">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.119*"/>
<RowDefinition Height="0.881*"/>
</Grid.RowDefinitions>
<TextBlock TextWrapping="Wrap" Text="{Binding tokenNo}" />
<StackPanel Grid.Row="1" Width="Auto" HorizontalAlignment="Stretch">
<ItemsControl Height="Auto" ItemsSource="{Binding tokenItems}" HorizontalAlignment="Stretch" Width="Auto">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Height="38" Width="Auto" Style="{StaticResource redButton}" HorizontalContentAlignment="Stretch" Click="listClick" >
<TextBlock Text= "{Binding name}"/>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" x:Name="tokenListBox" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
The program compiles well without any error. However when I update the TokenList it shows error with NullReference exception somewhere in XAML parsing.
I tried removing the Click event property from the XAML without any click event assigned. This executed program very well. But I can't click on the button which is the most thing I want. Further, I need custom style as well.
I am unable to figure out what the problem is... A quick fix will be highly appreciated.
EDIT:
This updates the tokenList;
public void update() {
List<TokenItems> tempList = new List<TokenItems>();
tokenList.Clear(); // clears previous items in tokenList;
while(database.Read()) {
tempList.Add(new Token() {
item= (int)database["item"]
});
}
var temp = new List<BumpBar.TokenItems>();
temp.AddRange(tokenModify(tempItems)); // modifies the items within the list
tempItems.Clear(); // clear to refill the tempItems
tempItems.AddRange(temp);
tokenList.Add(new Token() {
tokenNo = tokensList[i].tokenNo,
tokenItems = tempItems,
startedOn = tokensList[i].startedOn
});
}

Post the code that relates to this comment. I'm guessing you're accidentally nullify that token list.
The program compiles well without any error. However when I update the TokenList it shows
error with NullReference exception somewhere in XAML parsing.

Related

Showing a single value from api call TextBlock (MVVM)

I have been stuck on this for a few hours now. I am using the IGDB api for the application I'm building. So, I'm trying to get the ListBox I have to show the Game Title, cover and Developer. I've managed to get all of them to show up in the list with no issues but the main issue comes in when I just want to show a single developer within the list, the list is showing all companies involved plus the developer because the API returns an array of Involved Companies and within those Involved Companies the developer body returns a Boolean value to say which one is the developer. I can see it in my head, if developer is true then show the developer and remove the other involved companies. Here is an image of what is showing up when I run my query:
Query Image
I have outlined it in red. From that list all I would need is Bungie since they're the developers. So in my view I have a ListBox and a ListView nested within it:
BrowseGamesView
<ListBox ItemsSource="{Binding Games}"
Width="390"
Height="500"
Grid.Row="4">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
Margin="0 0 10 0">
<Image Source="{Binding cover.image_id, Converter={StaticResource stringToImage}}"
Stretch="Fill"
Height="70"
Width="60"/>
</Border>
<TextBlock Text="{Binding name}"
FontWeight="Bold"
Grid.Column="1"
Grid.Row="0"/>
<ListView Grid.Column="1"
Grid.Row="1"
ItemsSource="{Binding involved_companies}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding company.name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Within my ViewModel I have a method for making the query and also holds the ObservableCollection (Games).
BrowseGamesViewModel
public ObservableCollection<Game> Games { get; set; }
public async void MakeQuery()
{
var games = await IGDBHelper.GetGames(Query);
Games.Clear();
foreach(var game in games)
{
Games.Add(game);
}
}
In my Helper class I get the games
IGDBHelper
public static async Task<List<Game>> GetGames(string query)
{
List<Game> games = new List<Game>();
string myJson = "search \"{0}\"; fields name, cover.url, cover.image_id, involved_companies.developer, involved_companies.company.name; limit 500;";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("user-key", "key hidden");
var response = await client.PostAsync(BASE_URL_GAMES, new StringContent(string.Format(myJson, query), Encoding.UTF8, "application/json"));
var responseString = await response.Content.ReadAsStringAsync();
games = JsonConvert.DeserializeObject<List<Game>>(responseString);
}
return games;
}
And all of models are concrete classes for the Json
public class Game
{
public int id { get; set; }
public Cover cover { get; set; }
public List<InvolvedCompany> involved_companies { get; set; }
public string name { get; set; }
}
public class InvolvedCompany
{
public int id { get; set; }
public Company company { get; set; }
public bool developer { get; set; }
}
public class Company
{
public int id { get; set; }
public string name { get; set; }
}
public class Cover
{
public int id { get; set; }
public string image_id { get; set; }
public string url { get; set; }
}
So, to reiterate, I need to somehow just show the developers name by removing the rest of the involved companies from the list. I have tried several things from converters to trying it in the converter class and each time I get exceptions for null.
I would add a new readonly property to the Game class:
public List<InvolvedCompany> DeveloperCompanies
{
get
{
return involved_companies.Where(c => c.developer == true).ToList();
}
}
And then you can bind this new property to your ListView:
<ListView Grid.Column="1"
Grid.Row="1"
ItemsSource="{Binding DeveloperCompanies}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding company.name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This way you would be showing only the developer companies but you would keep the information about all the involved companies as well, if you need to use them for another purpose.
I also suggest you to use PascalCase for the public members of a class. So, for example involved_companies would become InvolvedCompanies.
First, I would suggest to name your properties in PascalCase.
Now for your problem, I suggest to filter the companies by developer property and assign a new list of involving companies to the game before adding it to the list.
foreach(var game in games)
{
game.involved_companies = game.involved_companies.Where(e => e.developer == true).Tolist();
Games.Add(game);
}
Not tested but I believe this should work.

Not able to see binded items to listbox

I've created a ListBox with this structure:
<ListBox VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding EventInfo}">
how you can see I binded the EventInfo property that I valorize behind code. This property have the OnPropertyChange(); implementation as my other properties, and the value setted is got correctly. Anyway, I'm not able to display the binded source:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
<TextBlock Text="test" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
now the property League value isn't displayed also the value test. I really don't understand why. The League property exist, and also I've no error in xaml.
What I did wrong?
UPDATE:
public Models.EventInfo EventInfo
{
get { return _eventInfo; }
set
{
_eventInfo = value;
OnPropertyChanged();
}
}
and in the Model
public class EventInfo
{
public string League { get; set; }
public string Date { get; set; }
public string GameWeek { get; set; }
public string GameStart { get; set; }
public string FirstTime { get; set; }
public string SecondTime { get; set; }
public string Stadium { get; set; }
public List<MatchArbiter> Arbiter { get; set; }
}
Try this. You need to populate ItemsSource with a collection, not a single item. Instead of your existing EventInfo property, you need a collection property. I'm going to rename it to EventInfoItems to keep confusion to a minimum.
private ObservableCollection<Models.EventInfo> _eventInfoItems =
new ObservableCollection<Models.EventInfo>();
public ObservableCollection<Models.EventInfo> EventInfoItems
{
get { _eventInfoItems; }
set
{
_eventInfoItems = value;
OnPropertyChanged();
}
}
Now, somewhere, you're going to have to add some items to that collection if you want anything to appear in the list. You could create a few test items in your viewmodel constructor, just for the time being. Like this:
EventInfoItems.Add(new EventInfo { League = "NBA" });
EventInfoItems.Add(new EventInfo { League = "Premier League" });
EventInfoItems.Add(new EventInfo { League = "Serie A" });
XAML
<ListBox
VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding EventInfoItems}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Update
Turns out OP may have only one item. If that's the case, a ListBox is unnecessary. A ContentControl is the right control when you've got only one item and you want to display it with a DataTemplate. This XAML will use the original version of the EventInfo property:
public Models.EventInfo EventInfo
{
get { return _eventInfo; }
set
{
_eventInfo = value;
OnPropertyChanged();
}
}
XAML:
<ContentControl
VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Content="{Binding EventInfo}"
>
<ContentControl.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>

DataTemplate x:Bind namespace error

I have a question about Windows 10 UWP development using Visual Studio 2015.
I'm trying to use a DataTemplate for my GridView according to this tutorial. The problem I'm having is with my namespace.
I am not allowed to share my exact code for obvious reasons, but I'm wondering if one of you guys might have run into this before. I am getting almost the same error as this person (error code 0x09c4), except my DataTemplate is in my code-behind-file, not global like him/her. Along with that error I'm also getting the illusive "the _name does not exist in the namespace _namespace".
Here is a piece of my xaml file:
<Grid>
...
<GridView ItemsSource="{x:Bind ViewModel.AssessExItems}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:AssessExItem">
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
I know the DataTemplate is empty but even if I enter something there it still doesn't work. Here is my code-behind-file for this xaml file:
public sealed partial class AssessmentExample1Screen : Page
{
public AssessExItemViewModel ViewModel { get; set; }
public AssessmentExample1Screen()
{
this.InitializeComponent();
this.ViewModel = new AssessExItemViewModel();
}
}
public class AssessExItem
{
public int _assessment_id { get; set; }
public string name { get; set; }
public string surname { get; set; }
public string date { get; set; }
//public EmpAssessItem() { }
}
public class AssessExItemViewModel
{
private ObservableCollection<AssessExItem> exampleItems = new ObservableCollection<AssessExItem>();
public ObservableCollection<AssessExItem> AssessExItems { get { return this.exampleItems; } }
public AssessExItemViewModel()
{
//for (int i = 1; i < 3; i++)
//{
this.exampleItems.Add(new AssessExItem()
{
name = "Cat 777",
surname = "Botha",
date = "2015-03-22"
});
//}
this.exampleItems.Add(new AssessExItem()
{
name = "XZR 678",
surname = "Botha",
date = "2015-03-22"
});
this.exampleItems.Add(new AssessExItem()
{
name = "TBL 123",
surname = "Botha",
date = "2015-03-22"
});
}
}
Please help.
I reproduced your problem.
How to solve : Clean and build or rebuild the solution.And then I tested it,it works.
I guess the most possible reason of why it happened is build can update the file mainpage.g.cs which determined where to find the datatype.
<GridView ItemsSource="{x:Bind ViewModel.AssessExItems}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="local:AssessExItem">
<StackPanel Height="100" Width="100" Background="OrangeRed">
<TextBlock Text="{x:Bind name}"/>
<TextBlock Text="{x:Bind surname}" x:Phase="1"/>
<TextBlock Text="{x:Bind date}" x:Phase="2"/>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>

windows store app(passing two fields through a command parameter) on the click of a button

I am working on a windows store app. I want to be able to pass two parameter on the click of a button to another page.
I am able to pass one conveniently like the line below
CommandParameter="{Binding CustomerServiceRepresentativeId}"
How can I pass an additional field like the one below making two fields passed to the destination page.
Binding CustomerServiceRepresentativeName
I have had no luck passing the two.
<GridView x:Name="GVCSRList" Grid.Column="2" Grid.Row="2" ItemsSource="{Binding}" SelectionMode="None" ItemContainerStyle="{StaticResource GridViewItemStyle1}" Width="Auto" Margin="40,0,0,0">
<GridView.ItemTemplate>
<DataTemplate >
<Button Style="{StaticResource CSRProfile}" Margin="0,0,10,0" Click="ButtonBase_OnClick" CommandParameter="{Binding CustomerServiceRepresentativeId}">
<StackPanel Width="290" Height="45" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0">
<TextBlock Tag="CSRListName" Text="{Binding CustomerServiceRepresentativeName}" Style="{StaticResource CntMedTextBlockStyleBold}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,15"/>
</StackPanel>
</Button>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
try
{
var commandParameter = ((Button)sender).CommandParameter;
if (commandParameter != null)
this.Frame.Navigate(typeof(CSRProfile), commandParameter.ToString());
}
catch (Exception ex)
{
throw ex;
}
}
Bind to a new property that has both memebers:
E.g., right now you have:
class MyClass
{
public int CustomerServiceRepresentativeId { get; set; }
public string CustomerServiceRepresentativeName { get; set; }
}
Put those in a separate class, and add a property of that type to your class. E.g.:
class IdNameCombo
{
public int CustomerServiceRepresentativeId { get; set; }
public string CustomerServiceRepresentativeName { get; set; }
}
class MyClass
{
public IdNameCombo IdName { get; set; }
}
Then, you bind to the new property:
CommandParameter="{Binding IdName}"
To set or get the id or the name, you will need to type a little bit more:
MyClass myClass;
myClass.IdName.CustomerServiceRepresentativeId = 1;
myClass.IdName.CustomerServiceRepresentativeName = "Bob";

Binding list of objects to ItemsControl with custom ItemTemplate in MVVM

Current Setup
I have a custom class representing an installer file and some properties about that file, conforming to the following interface
public interface IInstallerObject
{
string FileName { get; set; }
string FileExtension { get; set; }
string Path { get; set; }
int Build { get; set; }
ProductType ProductType { get; set; }
Architecture ArchType { get; set; }
bool Configurable { get; set; }
int AverageInstallTime { get; set; }
bool IsSelected { get; set; }
}
My ViewModel has a ReadOnlyObservableCollection<IInstallerObject> property named AvailableInstallerObjects.
My View has a GroupBox containing the ItemsControl which binds to the aforementioned property.
<GroupBox Header="Products">
<ItemsControl ItemsSource="{Binding Path=AvailableInstallerObjects}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}"
VerticalAlignment="Center" Margin="5"/>
<TextBlock Text="{Binding Path=FileName}" Margin="5" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
The binding works correctly, except it's not user friendly. 100+ items are shown.
Need Help Here
I'd like to be able to use my collection of IInstallerObjects but have the View present them with the following ItemTemplate structure.
<GroupBox Header="Products">
<ItemsControl ItemsSource="{Binding Path=AvailableInstallerObjects}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}"
VerticalAlignment="Center" Margin="5"/>
<TextBlock Text="{Binding Path=ProductType}" Margin="5" />
<ComboBox ItemsSource="{Binding Path=Build}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
Basically I want to be able to group by the ProductType property, showing a list of the available products, with the ComboBox representing the available Build property values for IInstallerObjects of the ProductType.
I can use LINQ in the ViewModel to extract the groupings, but I have no idea how I'd bind to what I've extracted.
My research also turned up the possibility of using a CollectionViewSource but I'm not certain on how I can apply that to my current setup.
I appreciate your help in advance. I'm willing to learn so if I've overlooked something obvious please direct me to the information and I'll gladly educate myself.
If Build should be a collection type.
so your class should be structured like this as an example.
Public Class Customer
Public Property FirstName as string
Public Property LastName as string
Public Property CustomerOrders as observableCollection(OF Orders)
End Class
This should give you the expected results. Each item in the main items presenter will show first name last name and combobox bound to that customers orders.
I know it's simple but this should do.
All you have to do is declare a CollectionViewSource in your view and bind it to the ObservableCollection. Within this object you declare one or more GroupDescriptions which will split up the source into several groups.
Bind this source to the listbox, create a Template for the group description and you are done.
An example can be found here: WPF Sample Series – ListBox Grouping, Sorting, Subtotals and Collapsible Regions. More about CollectionViewSource can be found here: WPF’s CollectionViewSource
The description of your problem lead me to believe you are looking for some kind of colapsing / expanding / grouped / tree-view sort of thing.
XAML for the tree-view
<Window x:Class="WPFLab12.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:WPFLab12"
Title="MainWindow" Height="350" Width="525">
<Grid>
<GroupBox Header="Products">
<TreeView ItemsSource="{Binding Path=ProductTypes}">
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type loc:ProductType}"
ItemsSource="{Binding AvailableInstallerObjects}">
<TextBlock Text="{Binding Description}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type loc:InstallerObject}">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsSelected}"
VerticalAlignment="Center" Margin="5"/>
<TextBlock Text="{Binding Path=FileName}" Margin="5" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</GroupBox>
</Grid>
</Window>
What does that do? Well, it establishes a hierarchy of controls in the tree based on the type of data found. The first HierarchicalDataTemplate handles how to display the data for each class, and how they are related in the hierarchy. The second HierarchicalDataTemplate handles how to display each InstallerObject.
Code behind for the Main Window:
public partial class MainWindow : Window
{
public ReadOnlyObservableCollection<ProductType> ProductTypes
{
get { return (ReadOnlyObservableCollection<ProductType>)GetValue(ProductTypesProperty); }
set { SetValue(ProductTypesProperty, value); }
}
// Using a DependencyProperty as the backing store for ProductTypes. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ProductTypesProperty =
DependencyProperty.Register("ProductTypes", typeof(ReadOnlyObservableCollection<ProductType>), typeof(MainWindow), new UIPropertyMetadata(null));
public MainWindow()
{
this.InitializeComponent();
this.ProductTypes = new ReadOnlyObservableCollection<ProductType>(
new ObservableCollection<ProductType>()
{
new ProductType()
{
Description = "Type A",
AvailableInstallerObjects = new ReadOnlyObservableCollection<InstallerObject>(
new ObservableCollection<InstallerObject>()
{
new InstallerObject() { FileName = "A" },
new InstallerObject() { FileName = "B" },
new InstallerObject() { FileName = "C" },
})
},
new ProductType()
{
Description = "Type B",
AvailableInstallerObjects = new ReadOnlyObservableCollection<InstallerObject>(
new ObservableCollection<InstallerObject>()
{
new InstallerObject() { FileName = "A" },
new InstallerObject() { FileName = "D" },
})
}
});
this.DataContext = this;
}
}
This is totally cheating, though - normally the MainWindow.cs would not serve as the DataContext and have all this stuff. But for this example I just had it make a list of ProductTypes and populate each ProductType class with the InstallerObject instances.
Classes I used, note I made some assumptions and modified your class to suit this View Model better:
public class InstallerObject
{
public string FileName { get; set; }
public string FileExtension { get; set; }
public string Path { get; set; }
public int Build { get; set; }
public bool Configurable { get; set; }
public int AverageInstallTime { get; set; }
public bool IsSelected { get; set; }
}
public class ProductType
{
public string Description { get; set; }
public ReadOnlyObservableCollection<InstallerObject> AvailableInstallerObjects
{
get;
set;
}
public override string ToString()
{
return this.Description;
}
}
So, in MVVM, it seems to me that your current InstallerObject class is more of a Model layer sort of thing. You might consider transforming it in your ViewModel to a set of collection classes that are easier to manage in your View. The idea in the ViewModel is to model things similarly to how they are going to be viewed and interracted with. Transform your flat list of InstallerObjects to a new collection of hierarchical data for easier binding to the View.
More info on various ways to use and customize your TreeView: http://www.codeproject.com/Articles/124644/Basic-Understanding-of-Tree-View-in-WPF

Categories

Resources