UWP databinding - c#

I want to make layout with table on page. Right now I have:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" Margin="10,0,10,0">
<ListView
x:Name="NewsList"
HorizontalAlignment="Center">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Margin="15" Grid.Column="0" FontSize="25" Text="{Binding news_time}"/>
<TextBlock Margin="15" Grid.Column="1" FontSize="25" Text="{Binding title}"/>
<TextBlock Margin="15" Grid.Column="2" FontSize="25" Text="{Binding news_text}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</Grid>
I want to make binding for my ObservableCollection objects to add Grid rows dynamically to StackPanel tags. Here is my .cs file.
public class News
{
public int id;
public string news_time;
public string title;
public string news_text;
}
public sealed partial class TermsAndNews : Page
{
public ObservableCollection<News> newsCollection { get; set; } =
new ObservableCollection<News>();
// ...
// Method called on initialization
private async void GetData()
{
var httpClient = new HttpClient();
var responsetNews = await httpClient.GetAsync("http://localhost:3000/news");
responsetNews.EnsureSuccessStatusCode();
string jsonNews = await responsetNews.Content.ReadAsStringAsync();
this.newsCollection = JsonConvert.DeserializeObject<ObservableCollection<News>>(jsonNews);
NewsList.ItemsSource = newsCollection;
}
}
After running the code, nothing is displayed. Any ideas ?

Try changing
this.newsCollection = .....
to
ObservableCollection<News> tempobcol = ...
then iterating through and adding to newsCollection
Foreach (News n in tempobcol)
{ newsCollection.Add(n); }
and finally, removing
NewsList.ItemsSource = newsColleciton;
in order to add it to the XAML
<ListView x:Name:"NewsList" ItemsSource:="{Binding newsCollection}"...../>

Related

How can I use 2 data bindings for an ItemSource Data Template in WPF App

I have a counter which increments depending on a foreach Loop from:
public partial class UserControlCounter : UserControl, INotifyPropertyChanged
{
private int _scanStatusCounter;
public int ScanStatusCounter
{
get { return _scanStatusCounter; }
set { _scanStatusCounter = value; NotifyPropertyChanged(); }
}
public UserControlCounter()
{
InitializeComponent();
DataContext = this;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(getAll);
scanStatus.Text = "Persons " + ScanStatusCounter.ToString();
}
private async void getAll()
{
//grab data and iterate it
string[] string_array = new string[] { "a", "b", "c", "d", "e" }; // type = System.String[]
foreach (var i in string_array)
{
ScanStatusCounter++;
await Task.Delay(100);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and the Xaml:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Grid.Column="0" Name="myListBox" Height="40" ></ListBox>
<TextBlock Grid.Row="0" Grid.Column="1" Name="scanStatus" Height="40" Text="{Binding Path=ScanStatusCounter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBlock>
<Button Grid.Row="0" Grid.Column="2" Click="Button_Click" Height="40">Click Me</Button>
</Grid>
That works fine and increments "live", but my problem is that I need to get that now running inside a more complex Data Template.
I've tried to inject the Counter onClick with:
private async void Button_Click(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
var contentPresenter = (btn.TemplatedParent as ContentPresenter);
var ppStatusCounter = contentPresenter.ContentTemplate.FindName("scanStatus", contentPresenter) as TextBlock;
ppStatusCounter.Text = "Entrys found: " + ScanStatusCounter.ToString();
}
But then I get the Value only onClick not like a live incrementing Counter, that's my Data Template:
<UserControl.Resources>
<c1:NameList x:Key="NameListData"/>
<DataTemplate x:Key="NameItemTemplate">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Domaine"/>
<TextBox Grid.Row="1" Grid.Column="0" x:Name="txtDomainName" Text="{Binding Path=DomaineName}" Margin="0,0,5,0"></TextBox>
<Button Grid.Row="1" Grid.Column="1" Width="100" Height="30" Margin="5,5,5,5" x:Name="btnNames" Click="Button_Click" Content="Start Scan" />
<Grid Grid.Column="2" Grid.Row="1" Height="20">
<ProgressBar x:Name="pbStatus" Height="20" Width="100" Minimum="0" Maximum="100" Visibility="Hidden"/>
<TextBlock x:Name="pbStatusText" HorizontalAlignment="Center" VerticalAlignment="Center" Text="Scanning" Visibility="Hidden"/>
</Grid>
<TextBlock Grid.Column="3" Grid.Row="1" Name="scanStatus" Text="{Binding Path=ScanStatusCounter, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid>
<StackPanel Orientation="Horizontal" VerticalAlignment="top">
<StackPanel Orientation="Horizontal" Margin="0,20,0,0">
<ListBox x:Name="lstDomainNames"
Margin="5,5,5,5"
ItemsSource="{Binding Source={StaticResource NameListData}}"
ItemTemplate="{StaticResource NameItemTemplate}"
IsSynchronizedWithCurrentItem="True"/>
</StackPanel>
</StackPanel>
</Grid>
that's my Data Source Class, not much there:
public partial class NameList : ObservableCollection<SetCredentials>
{
private static Logger logger = LogManager.GetCurrentClassLogger();
public NameList() : base()
{
using var forest = Forest.GetCurrentForest();
Forest currentForest = Forest.GetCurrentForest();
DomainCollection domains = currentForest.Domains;
foreach (Domain objDomain in domains)
{
Add(new SetCredentials(objDomain.ToString()));
}
}
}
public class SetCredentials
{
private string domainName;
public SetCredentials(string domain)
{
this.domainName = domain;
}
public string DomaineName
{
get { return domainName; }
set { domainName = value; }
}
}
Do I have to add a second Data Binding source to my ItemSource or do I need another approach for this, for example in my TextBox Binding?

Get index of clicked row from a nested listview - UWP

I have a nested listview. When clicks on it's button I want to get it's row index. Now I got the index as -1 always.
<ListView x:Name="Mainlist" HorizontalAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:CurrentBooksList">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" ></ColumnDefinition>
</Grid.ColumnDefinitions>
<ListView x:Name="sublist1" ItemsSource="{x:Bind CurrentFoldersArray}" Grid.Column="0" BorderBrush="Black" BorderThickness="0,0,0,1" HorizontalAlignment="Stretch" >
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:CurrentFoldersList">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Foreground="Black" FontWeight="Bold" Grid.Column="0" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center" Text="{x:Bind BookCode}"/>
<ListView x:Name="sublist2" ItemsSource="{x:Bind CurrentBookArray1}" Grid.Column="1" HorizontalAlignment="Stretch" >
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:CurrentBusList1">
<Button Width="120" Height="40" Text="{x:Bind Lockbook}" Click="LockMyBook_btn_Click">
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
private void LockMyBook_btn_Click(object sender, RoutedEventArgs e)
{
int selectedIndx = Mainlist.SelectedIndex; //always return -1
}
How can I get the row number of mainlist view, when user clicks on it's inner listview's button?
Yes it runs well. By getting row number of mainlist, I want to access corresponding items in CurrentBooksList
Well, it's strange. If you're using a basic UWP button control, it doesn't have Text property, your code should not work. But I didn't care about this point, your question was not related to it.
In your button's click event handler, you could get its DataContext, then you could convert it to an CurrentBusList1 object. By this CurrentBusList1 object, you could do some judgements from your 'Mainlist' ItemsSource and get the selected CurrentBooksList object.
public sealed partial class MainPage : Page
{
public ObservableCollection<CurrentBooksList> currentBooksLists { get; set; }
public MainPage()
{
this.InitializeComponent();
currentBooksLists = new ObservableCollection<CurrentBooksList>();
ObservableCollection<CurrentFoldersList> currentFoldersLists = new ObservableCollection<CurrentFoldersList>();
currentFoldersLists.Add(new CurrentFoldersList() { BookCode = "abc123", CurrentBookArray1 = new ObservableCollection<CurrentBusList1>() { new CurrentBusList1() { Lockbook = "123abc" }, new CurrentBusList1() { Lockbook = "123def" } } });
currentFoldersLists.Add(new CurrentFoldersList() { BookCode = "def456", CurrentBookArray1 = new ObservableCollection<CurrentBusList1>() { new CurrentBusList1() { Lockbook = "def456" } } });
currentBooksLists.Add(new CurrentBooksList() { CurrentFoldersArray = currentFoldersLists });
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int index;
var subSelectedItem = ((FrameworkElement)sender).DataContext as CurrentBusList1;
foreach (var item in currentBooksLists)
{
foreach (var folder in item.CurrentFoldersArray)
{
foreach (var bus in folder.CurrentBookArray1)
{
if (bus == subSelectedItem)
{
index = currentBooksLists.IndexOf(item);
break;
}
}
}
}
}
}
public class CurrentBooksList
{
public ObservableCollection<CurrentFoldersList> CurrentFoldersArray { get; set; }
}
public class CurrentFoldersList
{
public string BookCode { get; set; }
public ObservableCollection<CurrentBusList1> CurrentBookArray1 { get; set; }
}
public class CurrentBusList1
{
public string Lockbook { get; set; }
}
<ListView x:Name="Mainlist" HorizontalAlignment="Stretch" ItemsSource="{x:Bind currentBooksLists}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:CurrentBooksList">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" ></ColumnDefinition>
</Grid.ColumnDefinitions>
<ListView x:Name="sublist1" ItemsSource="{x:Bind CurrentFoldersArray}" Grid.Column="0" BorderBrush="Black" BorderThickness="0,0,0,1" HorizontalAlignment="Stretch" >
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:CurrentFoldersList">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Foreground="Black" FontWeight="Bold" Grid.Column="0" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center" Text="{x:Bind BookCode}"/>
<ListView x:Name="sublist2" ItemsSource="{x:Bind CurrentBookArray1}" Grid.Column="1" HorizontalAlignment="Stretch" >
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:CurrentBusList1">
<Button Width="120" Height="40" Content="{x:Bind Lockbook}" Click="Button_Click">
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Binding simple data using DataContext in XAML to code not working?

I'm learning WPF, I want to pass data from some textboxes to code:
<Grid Margin="0,0,10,43" Name="dataFrm">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Column="0">Name</Label>
<Label Grid.Column="0" Grid.Row="1">Code</Label>
<Label Grid.Column="0" Grid.Row="2">Categry Id</Label>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="txtName" Text="{Binding Path=Name, UpdateSourceTrigger=LostFocus}" Width="162" Margin="0,0,0,10" HorizontalAlignment="Left"></TextBox>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="txtCode" Text="{Binding Path=Code, UpdateSourceTrigger=LostFocus}" Width="162" Margin="0,0,0,10" HorizontalAlignment="Left"></TextBox>
<TextBox Grid.Row="2" Grid.Column="1" x:Name="txtCatId" Text="{Binding Path=CategoryId, UpdateSourceTrigger=LostFocus}" Width="162" Margin="0,0,0,10" HorizontalAlignment="Left"/>
<Button Grid.Row="3" Grid.Column="1" x:Name="btnSave" Click="btnSave_Click" Width="142" Content="Save"/>
</Grid>
When I click save, I want to cast the retrieved data to a predefined class:
private void btnSave_Click(object sender, RoutedEventArgs e)
{
var productVm = (ClsProducts)dataFrm.DataContext;
MessageBox.Show(productVm.Name);
}
ClsProducts:
public class ClsProducts
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public int CategoryId { get; set; }
}
but the DataContext of dataFrm is null!
Make sure that you set the DataContext property of the Grid or its parent window:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ClsProducts();
}
}

How to handle item highlight in ListBox when using more than one ListBox using wpf c#

I am using more than one customise ListBox in my application.
I want that when I click on item in one of the ListBox then highlight in other ListBox should be remove .Means I want to select item in one of the ListBox at a time.I provide sample code here
MainWindow.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<view:ListBox_UserControl x:Name="Control_1"/>
</Grid>
<Grid Grid.Row="1">
<view:ListBox_UserControl x:Name="Control_2"/>
</Grid>
<Grid Grid.Row="2">
<view:ListBox_UserControl x:Name="Control_3"/>
</Grid>
</Grid>
<Grid Grid.Column="1" Background="Aqua">
</Grid>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Control_1.ListBox_ItemSource = List_1;
Control_2.ListBox_ItemSource = List_1;
Control_3.ListBox_ItemSource = List_1;
}
List<string> List_1 = new List<string>()
{
"Item_1",
"Item_2",
"Item_3",
"Item_4",
"Item_5"
};
}
ListBox_UserControl.xaml
<Grid>
<ListBox Name="ListBox_1">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding }" MouseLeftButtonDown="TextBlock_MouseLeftButtonDown"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
ListBox_UserControl.xaml.cs
public partial class ListBox_UserControl : UserControl
{
protected event MouseButtonEventHandler TextBlock_Click;
public ListBox_UserControl()
{
InitializeComponent();
}
public List<string> ListBox_ItemSource
{
set
{
ListBox_1.ItemsSource = value;
}
}
private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MouseButtonEventHandler handler = TextBlock_Click;
if(handler!=null)
{
handler(sender, e);
}
}
}

Multiple Datatemplates & Grid.IsSharedSizeScope - Not aligning to content width

I have a listbox in which I render data using multiple datatemplates, and I use a datatemplate selector to route data to the appropriate template.
Each template has it's own layout using a grid. The first column of every grid inside the template is a textblock and I want them aligned to left. The next item is another textblock which should be aligned towards the maximum width of the first textblock (something similar to a data entry form). I'm using Grid.IsSharedSizeScope for this, but not able to achieve this. Below is my code:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<DataTemplate x:Key="DefaultTemplate">
<Border BorderThickness="1" CornerRadius="3" BorderBrush="LightGray">
<TextBlock Text="{Binding Name}"></TextBlock>
</Border>
</DataTemplate>
<DataTemplate x:Key="ShortFieldTemplate">
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="A"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Age:" Grid.Column="0"></TextBlock>
<TextBlock Text="{Binding Age}" Grid.Column="1"></TextBlock>
</Grid>
</DataTemplate>
<DataTemplate x:Key="LongFieldTemplate">
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="A"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="This should be the name:" Grid.Column="0"></TextBlock>
<TextBlock Text="{Binding Name}" Grid.Column="1"></TextBlock>
</Grid>
</DataTemplate>
<GridSplitterTextTrim:MyFirstTemplateSelector x:Key="MyFirstTemplateSelector"
DefaultDataTemplate="{StaticResource DefaultTemplate}"
ShortFieldDataTemplate="{StaticResource ShortFieldTemplate}"
LongFieldDataTemplate="{StaticResource LongFieldTemplate}"
/>
</Page.Resources>
<Grid x:Name="LayoutRoot" Grid.IsSharedSizeScope="True">
<Grid Grid.Column="2" Background="Green" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Items}" Grid.Row="0" Grid.Column="0" x:Name="listbox" ItemTemplateSelector="{StaticResource MyFirstTemplateSelector}">
</ListBox>
</Grid>
</Grid>
</Page>
..and my object model
public class ShortField : INotifyPropertyChanged
{
private string _name;
public string Age
{
get { return _name; }
set
{
_name = value;
InvokePropertyChanged(new PropertyChangedEventArgs("Age"));
}
}
private string _currentValue;
public string CurrentValue
{
get { return _currentValue; }
set
{
_currentValue = value;
InvokePropertyChanged(new PropertyChangedEventArgs("CurrentValue"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
public static List<ShortField> GetShortFields()
{
return new List<ShortField>()
{
new ShortField() {Age = "10"},
new ShortField() {Age = "21"},
};
}
}
How do I get this alignment right? I thought Grid.IsSharedScope should do the trick. Is that correct or is there any other way?
Thanks in advance,
-Mike
try to set
<ListBox Grid.IsSharedSizeScope="True"

Categories