Accessing WPF component inside DataTemplate with Data Binding - c#

I have several UI elements in a DataTemplate bound to an ObservableCollection of Video objects. I want to call a method of a Video object when I click on the ContextMenuItem [Test] of the corresponding UI element.
Here is my XAML:
<ItemsControl Name="VideoUIElment" >
<ItemsControl.ItemTemplate>
<DataTemplate x:Uid="videoTemplate">
<Border CornerRadius="10" Padding="10, 10" Background="Silver" >
<TextBlock Name="label" Text="{Binding Name}" FontSize="30" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="[TEST]" Name="Test" Click="Test_Click"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the collection:
public MainWindow()
{
//ctor
InitializeComponent();
pathToLauncher = string.Empty;
videos = new ObservableCollection<Video>();
VideoUIElment.ItemsSource = videos;
}
I know that, to do this, I have to identify which Video object inside the collection is bound to the specific UI element I click on, and I could come up with some trick to achieve this, but I would like to do it in a graceful and intelligent way.
I've seen some suggestions already, but none of them seemed to have been applicable here. I guess, it is supposed to be something easy, but I'm not very versed in WPF yet.

Try this:
MainWindow:
public partial class MainWindow : Window
{
ObservableCollection<Video> videos { get; set; }
public MainWindow()
{
InitializeComponent();
videos = new ObservableCollection<Video>
{
new Video {Name = "Video 1"},
new Video {Name = "Video 2"},
new Video {Name = "Video 3"}
};
VideoUIElment.ItemsSource = videos;
}
private void Test_Click(object sender, RoutedEventArgs e)
{
MenuItem item = (MenuItem)sender;
Video video = (Video)item.DataContext;
MessageBox.Show(video.VideoMethod());
}
}
Video:
public class Video
{
public string Name { get; set; }
public string VideoMethod()
{
return string.Format(" Clicked {0}", Name);
}
}

Related

First time developing in XAML, cannot make my chat work

First time I program an app in XAML, I learned from different sources and I am trying to make an app. Right now I am working on making a good looking chat but ran into an error that I cannot solve at all.
The part that bugs start in the MainWindow:
<chat:ChatViewModel/>
This calls the user control CharViewModel that creates the chat. Here is the XAML part of this user control:
<UserControl.Resources>
<DataTemplate DataType="{x:Type local:IncomingMessage}">
<Grid >
<Border Background="Orange" CornerRadius="15 15 0 15" Margin="10 12">
<TextBlock Margin="15" TextWrapping="Wrap" Foreground="CadetBlue" Text="{Binding MessageContent}"/>
</Border>
<TextBlock Text="Thu 5:45PM" HorizontalAlignment="Left" VerticalAlignment="Bottom" FontSize="10" Margin="10 0"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:OutgoingMessage}">
<Grid >
<Border Background="Gray" CornerRadius="15 15 0 15" Margin="10 12">
<TextBlock Margin="15" TextWrapping="Wrap" Foreground="CadetBlue" Text="{Binding MessageContent}"/>
</Border>
<TextBlock Text="Thu 5:45PM" HorizontalAlignment="Right" VerticalAlignment="Bottom" FontSize="10" Margin="10 0"/>
</Grid>
</DataTemplate>
</UserControl.Resources>
<UserControl.DataContext>
<local:MessageList/>
</UserControl.DataContext>
<Grid>
<ItemsControl ItemsSource="{Binding Messages}"/>
</Grid>
Here is the C# code:
public partial class ChatViewModel : UserControl
{
public ChatViewModel()
{
InitializeComponent();
}
}
This is how my MessageList looks like :
class MessageList : ViewModelBase
{
public MessageList()
{
Messages = new ObservableCollection<Message>();
}
public ObservableCollection<Message> Messages { get; set; }
}
To finish, this is the code in the MainWindow.cs that creates the list of message from a text file. Each message is of the form "David/Hi how are u?" in the text file:
var ListMessage = new MessageList();
using (StreamReader file = new StreamReader("../Utilisateur/Chat.txt"))
{
string text;
while ((text = file.ReadLine()) != null)
{
string[] list = text.Split('/');
if (list[0] == "David")
{
ListMessage.Messages.Add(new OutgoingMessage { MessageContent = list[1] });
}
else
{
ListMessage.Messages.Add(new IncomingMessage { MessageContent = list[1] });
}
}
}
With the message class looking like this:
public class Message : ViewModelBase
{
///Sender Name
public string SenderName { get; set; }
///Text of the message
private string _messageContent;
public string MessageContent
{
get
{
return _messageContent;
}
set
{
_messageContent = value;
OnPropertyChanged("MessageContent");
}
}
///True if message has been read
public bool MessageRead;
///Time the message was sent
public DateTimeOffset MessageSentTime { get; set; }
}
And the ViewModelBase that I copied from the internet because I don't fully understand how it works
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propName));
}
}
}
Here I have a text file with each line representing a message of the form "NameOfTheTender/Message".
Then I want to display the messages as small bubble, orange and on the left side of the window if I received the message, gray and on the right side if I sent it. For the moment nothing shows on the window.
How the window looks like right now
<local:MessageList/> in the UserControl's DataContext and new MessageList() in the Window's code behind create two different MessageList instances. Adding items to one of them has no effect on the other.
Your UserControl should not set its own DataContext. Remove this DataContent assignment from the UserControl's XAML:
<UserControl.DataContext>
<local:MessageList/>
</UserControl.DataContext>
In order to make the UserControl bind to the MessageList instance created in the MainWindow, just assign that to the Window's DataContext:
var ListMessage = new MessageList();
DataContext = ListMessages;
The value of the DataContext property will now be inherited by the UserControl.

How to reference a progressbar inside a CollectionView?

I have a CollectionView, on its data template has a progressbar. I'm able to find the respective element index of ObservableCollection but how can I reference its respective ProgressBar view? I need call method ProgressTo(), or may I simply bind the progress property to a property of the item on collection?
I'm afraid hat you can not use ProgressTo directly, because you can not access Progreeebar control in CollectionView directly.
If you still want to get ProgressBar, and call ProgressTo() method, you can consider to add Button in CollectionView datatemplate, like this:
<CollectionView ItemsSource="{Binding barmodels}" SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding str}" />
<ProgressBar Progress="{Binding value}" />
<Button Clicked="Button_Clicked" Text="btn1" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Then you can get current ProgressBar control by Button.Click.
public partial class Page7 : ContentPage
{
public ObservableCollection<barmodel> barmodels { get; set; }
public Page7()
{
InitializeComponent();
barmodels = new ObservableCollection<barmodel>()
{
new barmodel(){str="test 1",value=0.1},
new barmodel(){str="test 2",value=0.2},
new barmodel(){str="test 3",value=0.3},
new barmodel(){str="test 4",value=0.4},
new barmodel(){str="test 5",value=0.5}
};
this.BindingContext = this;
}
private void Button_Clicked(object sender, EventArgs e)
{
// access buttonclickhandler
var buttonClickHandler = (Button)sender;
// access Parent Layout for Button
StackLayout ParentStackLayout = (StackLayout)buttonClickHandler.Parent;
ProgressBar progressbar = (ProgressBar)ParentStackLayout.Children[1];
progressbar.ProgressTo(0.75, 500, Easing.Linear);
}
}
public class barmodel
{
public string str { get; set; }
public double value { get; set; }
}
But I don't suggest you to do it, I think use binding Progress for ProgressBar is the best way.

Binding an image to ImageBrush

i am having an issue while trying to bind an image to an ImageSource. I have tried some of the other fix on stackoverflow but none of them works.
I seem to get an error on this line saying that collection "Items" must be empty.
ImageList.ItemsSource = List;
The bind works well while using the "url" member of the FlickrData class.
MainWindow.xaml
<ScrollViewer>
<ListView x:Name="ImageList" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<Rectangle Margin="5" Width="100" Height="100">
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding imageBinding}"/>
</Rectangle.Fill>
</Rectangle>
</ListView>
</ScrollViewer>
FlickrData Class:
public class FlickrData
{
public String url { get; set;}
public FlickrData(Photo photo)
{
url = photo.SmallUrl;
}
public ImageBrush imageBinding
{
get
{
ImageBrush brush = new ImageBrush();
brush.ImageSource = new BitmapImage(new Uri(url));
return brush;
}
}
}
MainWindow class:
public partial class MainWindow : Window
{
public ObservableCollection<FlickrData> List = new ObservableCollection<FlickrData>();
public static Flickr flickr = new Flickr("XXXXXXXXXXXXXX");
public MainWindow()
{
InitializeComponent();
}
public void SearchWithInput(object sender, RoutedEventArgs e)
{
var options = new PhotoSearchOptions { Tags = SearchInput.Text, PerPage = 20, Page = 1 };
PhotoCollection photos = flickr.PhotosSearch(options);
List.Clear();
foreach (Photo photo in photos)
{
String flickrUrl = photo.WebUrl;
Console.WriteLine("Photo {0} has title {1} with url {2}", photo.PhotoId, photo.Title, photo.WebUrl);
List.Add(new FlickrData(photo));
}
ImageList.ItemsSource = List;
}
}
Do this to clean up the process
Change your XAML's ListView to ItemsSource="{Binding List}" which only needs to be done once.
Remove the now redundant ImageList.ItemsSource = List;
The list control will update itself accordingly because an ObservableCollection sends notifications of the change which are subscribed to by the list control.

How to Bind dynamic values in Xaml using WPF in c#

I have created application in WPF with two window. In one window i have used with one text box and submit button. Once submit from first window i hide first window and show second window. I have taken some values using first window text value and need to bind in second window Xaml. Actually that values can bind using foreach in html(mvc) but need to bind Xaml for display in second window. Please give some suggestions.
Please find the below answer, It will work definitely
Xaml :
<ItemsControl Name="icTodoList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="200,50,0,30">
<TextBlock>
<Hyperlink TextDecorations="None" NavigateUri="{Binding UriPath}" RequestNavigate="Hyperlink_RequestNavigate"
CommandParameter="{Binding ElementName=myImg}">
<Image HorizontalAlignment="Left" Width="80" Height="80" x:Name="myImg" Source="{Binding Source}" Margin="5"/>
</Hyperlink>
</TextBlock>
<TextBlock TextAlignment="Left" Margin="200,30,0,0">
<TextBlock FontSize="22px" Text="{Binding Title}" Foreground="white"></TextBlock>
</TextBlock>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
C# code for binding Values,
public class DMScreen3 {
public List<string> AllFiles { get; set; }
List<BindingFilesContent> items = new List<BindingFilesContent>();
if(AllFiles != null)
{
foreach(var r in AllFiles)
{
if ((r.ToLower().Contains(".avi") || r.ToLower().Contains(".mp4")) && fileTypes == "video")
{
items.Add(new BindingFilesContent() { Title = Path.GetFileName(r), UriPath = r, Source = "/images/videoicon.png" });
}
icTodoList.ItemsSource = items;
}
}
}
public class BindingFilesContent
{
public string Title { get; set; }
public string Source { get; set; }
public string UriPath { get; set; }
}

Bind List<Object> to a comboxbox in c# wpf

I have a static class named Building which contains a List<Beam> Beams as its property;
public static class Building
{
public static readonly List<Beam> Beams = new List<Beam>();
}
public class Beam
{
public string Story;
public double Elevation;
}
I'm trying to Bind the Building.Beams to a combobox in XAML so that Elevation and Story properties of each item in Building.Beams list is displayed in different columns in the combobox. I have been able to implement the two columns, I just can't Bind these properties.
Here is what I have tried so far:
<ComboBox x:Name="cmbBuilding" ItemsSource="{Binding}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Width="300">
<TextBlock Width="150" Text="{Binding Path=Story }"/>
<TextBlock Width="150" Text="{Binding Path=Elevation}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
var b1 = new Beam { Elevation = 320, Story = "ST1" };
var b2 = new Beam { Elevation = 640, Story = "ST2" };
Building.Beams.Add(b1);
Building.Beams.Add(b2);
First of all you can't bind with fields.
Convert Story and Elevation to properties (automatic properties in your case will do)
public class Beam
{
public string Story { get; set;}
public double Elevation { get; set;}
}
Second, you should use ObservableCollection in case you are adding items to the list after loading finishes so that UI gets notification.
public static readonly ObservableCollection<Beam> Beams
= new ObservableCollection<Beam>();
Try this example:
XAML
<Grid>
<ComboBox x:Name="cmbBuilding" Width="100" Height="25" ItemsSource="{Binding Path=Beams}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Width="300">
<TextBlock Width="150" Text="{Binding Path=Story}" HorizontalAlignment="Left" />
<TextBlock Width="150" Text="{Binding Path=Elevation}" HorizontalAlignment="Right" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="Add item" VerticalAlignment="Top" Click="Button_Click" />
</Grid>
Code-behind
public partial class MainWindow : Window
{
Building building = new Building();
public MainWindow()
{
InitializeComponent();
building.Beams = new List<Beam>();
building.Beams.Add(new Beam
{
Elevation = 320,
Story = "ST1"
});
this.DataContext = building;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var b1 = new Beam { Elevation = 320, Story = "ST1" };
var b2 = new Beam { Elevation = 640, Story = "ST2" };
building.Beams.Add(b1);
building.Beams.Add(b2);
cmbBuilding.Items.Refresh();
}
}
public class Building
{
public List<Beam> Beams
{
get;
set;
}
}
public class Beam
{
public string Story
{
get;
set;
}
public double Elevation
{
get;
set;
}
}
Some notes
When you use properties in the Binding, you need to be properties with get and set, not fields.
Properties, what were added to the List<T> will automatically update, you should call MyComboBox.Items.Refresh() method, or use ObservableCollection<T>:
ObservableCollection represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
Is it maybe because you have declared Beams as readonly yet you try to ADD items to it? Beams is also defined as a variable, try removing the readonly and making it a property with a getter and setter

Categories

Resources