Bind button content to a variable - c#

I have the following in XAML:
<Button Content="{Binding KB.Text}" />
KB is an instance of the following class:
public class ButtonText
{
public string Text
{
get
{
return "Button";
}
}
}
I have KB defined as global variable in the code behind of the page, the button content is showing empty when running the project, how would I achieve this? The button content should be retrieved by KB.Text

Make sure that your "KB" object is initialized (not null).
You might have missed "this.DataContext = this" in your main function
Make sure your KB is property
This works for me:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
KB = new ButtonText();
}
public ButtonText KB { get; }
}
public class ButtonText
{
public string Text
{
get
{
return "Button";
}
}
}
EDIT:
I wrote the first solution having in mind WPF, took me a while to figure there's a "UWP" tag.
In UWP, if you want to bind something to the code behind of the designer itself (*xaml.cs), you should use "x:Bind" instead of "Binding".
See the link about x:Bind vs Binding
In short, your xaml should look like so:
<Button Content="{x:Bind KB.Text}"/>

Related

How to set title of a page that hosted by a frame window?

I have a frame window that are used to host pages.
//inside frame window
public partial class FrameWindow : Window
{
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Main.Content = new Example_Page();
}
}
I saw this solution but its not worked since the page hosted by a window frame. So how do i set a title of a page and display on the top bar of the window? thank you
You can make your Example_Page expose a property (for example Title) that denotes the title and then make the FrameWindow Title property bind to that property. An example:
In the markup for your FrameWindow bind the title like this:
<Window ...
Title="{Binding Content.Title, RelativeSource={RelativeSource Self}}">
And in your Example_Page code behind expose a property like this:
public partial class Example_Page : ....
{
...
public string Title { get; set; }
...
}
Note that if you set the Content of the FrameWindow to any content that doesn't expose this property the Title will be blank.

Init some static resources in codebehind and have access to them in XAML (WPF)

I have a big image that I load to memory once ,split this image to multiple BitmapImage and from this point on there will be no changes to those images.
With this part i'm done: Initializing this list right after InitializeComponent(); of the Window partial class..
The only problem is , I cannot access to this list from my XAML file. I have tired so many different ways described online but none worked.
I am asking this question as a general issue and not a specific error I get cause there might be a better way to achieve the same purpose using C#/wpf mechanisms that i'm not aware of.. (i'm mainly doing java)
Thanks!
EDIT1: (after implementing #Janne Matikainen solution)
Now I get this error (not positive its related)
where the Uri for the image I use is :
new Uri(#"pack://application:,,,/Images/myimage.bmp", UriKind.Absolute)
and the folder Images lays in the project root.
Is it related to our issue?
EDIT 2 :
The warning for the URI is just a warning - It basically says it cannot find the URI since the link to the bitmaps will be built in runtime , and it cannot "find" it during compile time..
You should not create resource dynamically. If you do, it has to be done before you call InitializeCompontent() and you need to access the resource using {DynamicResource ResourceKey} markup extension.
You can use Binding with RelativeSource to access properties defined in code behind:
<ItemsControl ItemsSource="{Binding MyBitmapSources, RelativeSource={RelativeSource AncestorType=local:MainWindow}}" />
public partial class MainWindow : Window
{
public ObservableCollection<BitmapImage> MyBitmapSources { get; private set; }
public MainWindow()
{
MyBitmapSources = new ObservableCollection<BitmapImage>();
InitializeComponent();
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
//set uri, or streamsource here
bitmapImage.EndInit();
MyBitmapSources.Add(bitmapImage);
}
}
However, if you use binding, you should initialize the properties before you call InilializeComponent(). Otherwise you need to notify UI that the property has changed. Or in our case, that the collection has changed. I have used ObservableCollection, because it notifies UI about changes automatically.
EDIT:
If you want to share the BitmapImage between multiple views, just use singleton pattern or similar:
public partial class MainWindow : Window
{
public ObservableCollection<BitmapImage> MyBitmapSources
{
get { return MySingleton.Instance.MyBitmapSources; }
}
or you can store the collection in application resources:
public partial class MainWindow : Window
{
public ObservableCollection<BitmapImage> MyBitmapSources
{
get { return (ObservableCollection<BitmapImage>)Application.Current.Resources["MyBitmapSources"]; }
set { Application.Current.Resources["MyBitmapSources"] = value; }
}
First you need a static view model that you can bind from within your view(s). Then you load the image and split it within the private constructor and expose the Image or collection, depending on the binding approach you take.
public class StaticImageViewModel
{
private static readonly Lazy<StaticImageViewModel> Lazy = new Lazy<StaticImageViewModel>(() => new StaticImageViewModel());
private List<BitmapImage> images;
private StaticImageViewModel()
{
this.images = new List<BitmapImage>
{
new BitmapImage(new Uri("pack://application:,,,/WpfApplication1;component/Images/Image1.png"))
};
}
public static StaticImageViewModel Instance
{
get { return Lazy.Value; }
}
public BitmapImage Image
{
get
{
return this.images[0];
}
}
}
And then you can bind from this static viewmodel like this, you just need to make a valueconverter or such to get image with specific name/index from your dictionary/list.
<Image Source="{Binding Source={x:Static local:StaticImageViewModel.Instance}, Path=Image}"></Image>

Alternative to hyperlink to navigate?

I'm using WPF (.net 4.5 target) and while i'm quite used to WPF this is the first time i'm working with the page / navigation model.
I see that to navigate to a page i can do something very simple (use an hyperlink) however this is very far from what i want in terms of style (i'd rather start from a button). Is there an alternative to hyperlinks or is my best shot to go with a button + code behind to navigate?
My goal is to provide styled buttons with icons as hyperlinks within a uniformgrid that takes the whole page for the home page.
In Silverlight/WP, there is HyperlinkButton, which is exactly what you're asking for, but apparently that does not exist in WPF. So, it seems that your best bet would be to use the regular Button, along with NavigationService.Navigate in its Command. So, something like this:
<Button Content="Navigate" Command="{Binding NavigateCommand}"
CommandParameter="/Views/SomePage.xaml" />
Then NavigateCommand would be a standard DelegateCommand, using the parameter as the URI:
public class MyViewModel : ViewModelBase
{
public ICommand NavigateCommand { get; private set; }
public MyViewModel()
{
NavigateCommand = new DelegateCommand<string>(url => {
var uri = new Uri(url, UriKind.Relative);
NavigationService.Navigate(uri);
});
}
}
You may instead want to write your own version of HyperlinkButton, which would go something like this:
public class HyperlinkButton : Button
{
public string NavigateUri { get; set; }
protected override void OnClick(EventArgs e)
{
if (NavigateUri != null)
{
var uri = new Uri(NavigateUri, UriKind.Relative);
NavigationService.Navigate(uri);
}
}
}

Binding an ObservableCollection to ListBox (Easy case?)

I am really struggling to understand binding. I know there are loads of other threads with much the same title as this one, but they're all trying to do something more complex than I am, and all the answers assume a whole pile of stuff that I just don't get :(
I'm trying to display a dynamically updated message log. I've defined a Message class:
public class Message
{
public DateTime Timestamp { get; private set; }
public string Value { get; private set; }
public int Severity { get; private set; }
public Message(string value, int severity)
{
Timestamp = DateTime.Now;
Value = value;
Severity = severity;
}
}
I've defined a MessageLog class as simply:
public class MessageLog: ObservableCollection<Message>
{
public MessageLog(): base()
{ }
}
In my MainWindow constructor I have a Log property:
public MessageLog Log { get; private set; }
In the MainWindow constructor I initialise Log:
public MainWindow()
{
InitializeComponent();
DataContext = this;
Log = new Model.MessageLog();
// and so on
}
In the XAML for the main window I have:
<ListBox Name="MessagePanel" Height="100" ItemsSource="{Binding MessageLog}" IsEnabled="False"/>
Now if I add Message instances to the MessageLog I expected to see them appear in the ListBox. They don't. What have I missed?
Thanks in advance (and if you can point me somewhere that explains bindings clearly -- especially the view that XAML has of the code and where it can look for things -- then many more thanks on top. At the moment I'm using Matthew McDonald's "Pro WPF 4.5 in C#" and I'm just not getting it.)
Change your constructor:
public MainWindow()
{
InitializeComponent();
DataContext = this;
Log = new Model.MessageLog();
}
to this:
public MainWindow()
{
InitializeComponent();
Log = new Model.MessageLog(); // <- This line before setting the DataContext
DataContext = this;
}
Explanation:
Setting properties after having set the DataContext requires your class to implement INotifyPropertyChanged and raise change notifications after properties are set.
Since you're setting the DataContext before setting the property, the value of this.Log is null at the time of DataBinding, and WPF is never notified that it ever changed.
That being said, you don't usually put Data inside UI Elements (such as Window). The accepted and recommended approach to WPF is MVVM, where you usually create a ViewModel and set that as the Window's DataContext:
public class MyViewModel
{
public MessageLog Log {get;set;}
public MyViewModel()
{
Log = new MessageLog();
}
}
Window Constructor:
public MainWindow
{
DataContext = new MyViewModel();
}
Your collection property name is Log which is what you should be binding to in ItemsSource property; and if you have not done a typo in your question then you are binding wrongly to MessageLog, and change Binding as below:
<ListBox Name="MessagePanel" Height="100" ItemsSource="{Binding Log}" IsEnabled="False"/>
For more information and learning on Data Binding in WPF (4.5), see MSDN Data Binding Overview
The datacontext of the view must be the viewmodel.

ListBox of dead simple MVVM application stays empty - what am I missing?

The XAML of my window looks like this:
<Window x:Class="Binding1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Cronjobs" Height="350" Width="525">
<Grid>
<ListBox HorizontalAlignment="Stretch" Margin="10" VerticalAlignment="Stretch" ItemsSource="{Binding Cronjobs}" />
</Grid>
</Window>
As visible I bind the ListBox's ItemsSource to the Cronjobs property of the current DataContext. The DataContext is set to an instance of the ViewModel below in the constructor of the code-behind:
public partial class MainWindow : Window
{
private CronjobViewModel cronjobViewModel;
public MainWindow()
{
InitializeComponent();
this.cronjobViewModel = new CronjobViewModel();
this.DataContext = cronjobViewModel;
}
}
The ViewModel looks like this:
class CronjobViewModel : DependencyObject
{
public ObservableCollection<Cronjob> Cronjobs;
public CronjobViewModel( )
{
this.Cronjobs = new ObservableCollection<Cronjob>();
this.Cronjobs.Add( new Cronjob() );
this.Cronjobs.Add( new Cronjob() );
}
}
For quick and simple debugging I manually add some items to the collection for now. That Cronjob class is the actual model which is nothing more than a class with some simple string properties, cut down to the essential part:
class Cronjob {
private string name;
public string Name { get { return this.name; } set { this.name = value; } }
public Cronjob( ) { this.Name = "Herp"; }
}
I am mainly experienced in web development and new to the combination of WPF and MVVM. I spent nearly 10 hours figuring this out now but still do not see the cause. I also tried the DataGrid. I watched the first half of Jason Dolingers Video about MVVM about three times and took a close look on how did it, but it does not work for me, even though I understood the abstract concept of MVVM. I am pretty sure I just unintendedly omitted something in the XAML which should be there, but messing around with display property names and item templates did not help (according to what I found here and there around the internet they are not even necessary). Does anybody see the error in this code?
Sorry for the large code dump, I formatted the "boring" parts in a more compact way.
It's because Cronjobs is a field and you cannot bind to fields. Try changing it into property:
public ObservableCollection<Cronjob> Cronjobs { get; set; }
This should work ;)
public class CronjobViewModel
{
public ObservableCollection<Cronjob> Cronjobs { get; private set; }
public CronjobViewModel()
{
this.Cronjobs = new ObservableCollection<Cronjob>();
this.Cronjobs.Add(new Cronjob());
this.Cronjobs.Add(new Cronjob());
}
}

Categories

Resources