Changing the text field of a button on creation in xaml - c#

I have an application which has support for multiple different languages and as such one of the requirements is that the user experience should not change regardless of language. I'm currently have a dialog box which has multiple buttons in English but is there a way to dynamically change the text fields of the buttons?

I would suggest a MarkupExtension that does all the heavy work:
public class TranlationExtension : MarkupExtension
{
public string Key { get; set; }
public TranlationExtension(string key)
{
this.Key = key;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
Binding binding = new Binding("TranslationDictionary[" + Key + "]");
binding.Source = MyTranslations;
return binding.ProvideValue(serviceProvider);
}
}
The MyTranslations must be a class that implements INotifyPropertyChanged if the Culture Changes.
Useage:
<Button Content="{local:Tranlation TheKeyOfTheTranslationThatIsUsedInTheDictionaryAswell }"/>
The Approach is described here http://www.wpftutorial.net/LocalizeMarkupExtension.html

You can bind content property of the button to property in your viewmodel, for example:
<Button Content="{Binding ButtonTxt}"/>
Then you can set ButtonTxt property in viewmodel regardles of selected language e.g.
if (languageId == Languages.English) {
ButtonTxt = buttonTextInEnglish;
}

Related

TextBlock Text Property only Binding once even Binding Mode Set to TwoWay

Source Code:
class Cons
{
public Cons()
{
BaseDir = AppDomain.CurrentDomain.BaseDirectory;
SetProperty();
}
private void SetProperty()
{
NowPlaying = "Hello";
}
public string NowPlaying{get; set; }
}
public partial class MainWindow
{
Cons Resources = new Cons()
public MainWindow()
{
txbl.DataContext = Resources;
Resources.NowPlaying = "NoHello";
}
}
And a text block <Textblock x:Name="txbl" Text="{Binding NowPlaying, Mode=TwoWay}"/>
When I set the NowPlaying Property in the MainWindow contructor, the Property changed to "NoHello", but the TextBlock Text Property still Hello even I set binding mode = TwoWay
The Image I Capture When the Problem Occured
The code you've mentioned works correctly. The textblock does show the new value "NoHello".
But, if you wish to update the the property value NowPlaying after your textblock has been loaded, please implement INotifyPropertyChanged on your class Cons and raise PropertyChanged in the setter of the property NowPlaying.

What is the proper way to create dependency properties in user controls that allow two way bindings

I say proper because all the examples I have seen all seem to contradict each other and or fall short in some respects. I need to be able to bind a class object to my DP from XAML or set it programmatically in cb:
<local:MyControl MyDP="{Binding MyObject}"/>
or
MyControl mycontrol = new MyControl(){MyDP = MyObject};
and then within the control, two way bind elements to properties of the binding object:
<TextBox text="{Binding MyDP.text, ElementName=MyControl}"/>
I would think this is pretty standard stuff, but the lack of cohesion I have seen in people trying to write examples has led me to believe otherwise.
This is how I do it:
Assume a parent control that contains a status bar (User Control) it's markup looks like this:
<ContentControl x:Name="XFoot" Grid.Row="1" Grid.ColumnSpan="3" >
<UserControls:UCStatusBar />
</ContentControl>
Now in the UserControl named status bar it's dependency property looks like this:
public StatusUpdate CurrentStatus
{
get { return (StatusUpdate)GetValue(CurrentStatusProperty); }
set { SetValue(CurrentStatusProperty, value); }
}
// Using a DependencyProperty as the backing store for CurrentStatus. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CurrentStatusProperty =
DependencyProperty.Register("CurrentStatus", typeof(StatusUpdate), typeof(UCStatusBar),
new PropertyMetadata(
new StatusUpdate() { ErrorMessage="", IsIndeterminate=false, Status="Ready"}
)
);
Status Update looks like this, it's just a container for three properties shown in the status bar.
public class StatusUpdate
{
public StatusUpdate()
{
Status = "";
ErrorMessage = "";
IsIndeterminate = true;
}
public string Status { get; set; }
public string ErrorMessage { get; set; }
public bool IsIndeterminate { get; set; }
}
}
Anyone can update the status bar by accessing the CurrentStatus property of the statusbar. NOTE to make a two way binding, that's done in XAML... within the binding directive, just press space bar and the property window will show binding modes. Pick two way.
PS: In Visual Studio, when you first create the DP just type in propdp and press the tab button, the entire DP structure is inserted for you automatically. As a result DPs are easy to implement.
How do DPs work two-way?
If you are using XAML binding you simply tell it via the MODE property that it's two way. This means that the GUI changes will update the properties when user changes the values.
<TextBox Text="{Binding Path=ThePropertyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Notice the Mode value and the UpdateSourceTrigger says, don't want for focus to change, update it right away.
Wait a minute, nothing is happening when I bind and make changes!
Three things are required for a binding to work 1) DataContext must be set either in code behind or as a static resource in the XAML 2) The pathname to the property name must exactly the CLR property name and 3) There must be content in the property.
Can I fire an Event from somewhere else to update the property?
Sure... the first step is to set up the static eventhandler in the UserControl like this:
public static EventHandler<string> NewData;
Then in the CTOR wire it up like this:
NewData+=OnNewData;
Then the event handler looks like this:
private void OnNewData(object sender, string data){
//setting this string property notifies WPF to update the GUI
ThePropertyName = data;
}
The other code does this...
MyUserControl.OnNewData(this, "Now is the time for all good men to...");

How can I dynamically load two of the same user control on runtime without allowing changes in one control to affect the other?

I'm having trouble re-using a user control in WPF and I hope you can help.
On runtime, I am creating new instances of this user control to appear in a WPF grid. The user control consists of a text box and a list box, where user input in the text box filters the results in the list box. Say for this example that I create two of this same user control dynamically at runtime which share the same ViewModel. However, when I run the application, if I enter something in the second user control text box, it reflects in the listbox of BOTH of the user controls. I only want it to reflect in its own listbox.
Here is the picture of the issue on the front end. The highlighted input is reflected in both list boxes but shouldn't:
Here is the User Control XAML:
<UserControl x:Class="SCM_AllergyRecModule.SearchAndSelectView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<StackPanel Width="150">
<TextBox x:Name="Filter" Text="{Binding Path=Filter, UpdateSourceTrigger=PropertyChanged}"/>
<ListBox Height="50"
ItemsSource="{Binding Path=Allergens}"
IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding Path=SelectedAllergen}">
</ListBox>
</StackPanel>
And the ViewModel for the User Control:
namespace SCM_AllergyRecModule
{
public class SearchAndSelectViewModel
{
private ICollectionView allergens;
private string selectedAllergen;
private string filter = "";
public string Filter
{
get
{
return this.filter.ToUpperInvariant();
}
set
{
if (this.filter != value)
{
this.filter = value;
this.Allergens.Refresh();
}
}
}
private bool ContainsFilter(object item)
{
var product = item as string;
if (product == null)
{
return false;
}
if (string.IsNullOrEmpty(this.Filter))
{
return true;
}
if (product.ToUpperInvariant().Contains(this.Filter))
{
return true;
}
return false;
}
public SearchAndSelectViewModel()
{
this.allergens = CollectionViewSource.GetDefaultView(MainWindow.scmAllergens);
this.allergens.Filter = ContainsFilter;
}
public ICollectionView Allergens
{
get
{
return this.allergens;
}
}
public string SelectedAllergen
{
get
{
return this.selectedAllergen;
}
set
{
if (this.selectedAllergen != value)
{
this.selectedAllergen = value;
}
}
}
}
}
Here is the dynamic loading of the User Control on runtime in the Main Window (please ignore the naming for now):
SearchAndSelectView ssvAllergenSearch = new SearchAndSelectView();
ssvAllergenSearch.DataContext = new SearchAndSelectViewModel();
controlName = "ssvAllergenSearch" + (rowCounter.ToString() + allergenColumn.ToString());
ssvAllergenSearch.Name = controlName;
this.RegisterName(controlName, cmbAllscriptsCategory);
Grid.SetRow(ssvAllergenSearch, rowCounter);
Grid.SetColumn(ssvAllergenSearch, allergenColumn);
DynamicGrid.Children.Add(ssvAllergenSearch);
I believe this is the offending line:
this.allergens = CollectionViewSource.GetDefaultView(MainWindow.scmAllergens);
I assume you only have one instance of MainWindow, and thereby only one instance of scmAllergens. By calling CollectionViewSource.GetDefaultView, you are getting the same CollectionViewSource both times. Manually create a new instance instead:
var cvs = new CollectionViewSource();
cvs.Source = MainWindow.scmAllergens;
this.allergens = cvs.View;
If they're sharing the same viewmodel, that's your issue. Your controls are being bound by DataContext to the same source, so the targets link back to the same instance which causes a change in one to change the other via two-way bindings. Instantiate separate models for each and your problem will be solved. Building the control as a concrete control (where the code-behind is also the viewmodel) eliminates this problem and is the MVVM best practice in most cases.

Bind object properties to a datagrid in WPF

I have the following class:
public class Sp3dItem
{
public Sp3dItem()
{
Items= new ObservableCollection<Sp3dItem>();
}
public string OID
{
get;
set;
}
public string Name
{
get;
set;
}
public string Type
{
get;
set;
}
public ObservableCollection<Sp3dItem> Items
{
get;
set;
}
}
I need to show the properties of an instance of this object to a Datagrid (or any other type of grid). Like the Properties Window in Visual Studio. But there are certain properties that I don't care, like 'Items', I only need to show properties of string Type, and only the ones with non empty values (this last one would be a plus, not a real need).
The question is, can I do something like this with binding or do I have to assembly the data on the grid manually?
Sounds like you want a property grid to view the properties of a single object instance, where each property/value pair is a 'row', yes? If that's the case, look into some of the third-party Property Grid controls. The WPF Extended Toolkit has a free one.
Typically, these grids can automatically discover the properties of the target object, and you can choose to hide certain properties by adorning them with [Browsable(false)].
Yes... it's possible and easy once you figure out how the built-in binding wizard works.
This example is for a
<Label...
Create a static instance to your view model in the View. By doing this the designer will show the properties of the Viewmodel in the
properties page once you start "wiring up the bindings"...
//in code behind
public static string Error
{
get { return _Error; }
set { _Error = value; }
}
Now click on the XMAL component in designer just once.
<Label Grid.Row="2" <=Click here one time
In the properties page, click the icon (small square on far right side of property) to start the binding process
Select "Create Data Binding"
Select 'FindAncestor' then the MainWindow of interest, and finally the static property.
Click ok and the bindings are set in XAML Automatically.
<Label Grid.Row="2"
Content="{
Binding Error,
RelativeSource={
RelativeSource FindAncestor,
AncestorType={x:Type local:MainWindow}}}"/>
The verbosity above just says:
Look in MainWindow's static properties for Error.
Make this Label's content that value.
If you want you can also edit the template for the Datagrid, but that's not relevant to your question.

MVVM DataBinding

I've started an MVVM project and now I'm stucking with correct DataBinding.
My project has:
A UserControl whit a ViewModel as DataContext like:
public partial class TestUserControl: UserControl
{
public TestUserControl()
{
this.DataContext = new TestUserControlViewModel();
}
}
ViewModel code is (BaseViewModel class contains PropertyChangedEventHandler):
public class TestUserControlViewModel : BaseViewModel
{
public KrankenkasseControlViewModel()
{}
public IEnumerable<DataItem> GetAllData
{
get
{
IGetTheData src= new DataRepository();
return src.GetData();
}
}
}
IGetTheData is the interface to DataContext:
public interface IGetTheData
{
IEnumerable<DataItem> GetData();
}
}
and finally the DataRepository code:
public class DataRepository : IGetTheData
{
private TestProjectDataContext dax = new TestProjectDataContext();
public IEnumerable<DataItem> GetData()
{
return (from d in this.dax.TestData
select new DataItem
{
ID = d.ID,
SomeOtherData = d.SomeOtherData
});
}
}
My UserControl has a few TextBoxes, but what's the best way to bind correctly?
Thanks for your help, regards.
EDIT: Binding the data against multiple textboxes
After reading your comment, I will elaborate my example for textboxes.
First important thing is that the ViewModel will model the things in the View, so that the View gets all information it needs in the structure it needs. That means, if you have multiple textboses in the View, you will need multiple string Properties in your ViewModel, one for each textbox.
In your XAML you could have something like
<TextBox Text="{Binding ID, Mode=TwoWay}" />
<TextBox Text="{Binding SomeOtherData, Mode=TwoWay}" />
and in your ViewModel
public class TestUserControlViewModel : BaseViewModel {
private string id;
private string someOtherData;
public TestUserControlViewModel() {
DataItem firstItem = new DataRepository().GetData().First();
this.ID = firstItem.ID;
this.SomeOtherData = firstItem.SomeOtherData;
}
public string ID {
get {
return this.id;
}
set {
if (this.id == value) return;
this.id = value;
this.OnPropertyChangedEvent("ID");
}
}
public string SomeOtherData {
get {
return this.someOtherData;
}
set {
if (this.someOtherData == value) return;
this.someOtherData = value;
this.OnPropertyChangedEvent("SomeOtherData");
}
}
}
Here I assume that in your BaseViewModel there is an OnPropertyChangedEvent method to fire the corresponding event. This tells the View that the property has changed and it must update itself.
Note the Mode=TwoWay in the XAML. This means, that it doesn't matter on which side the value changes, the other side will reflect the change immediately. So if the user changes a value in a TwoWay bound TextBox, then the corresponding ViewModel property will automatically change! And also vice versa: if you change the ViewModel property programmatically, the View will refresh.
If you want to show multiple textboxes for more than one data item, then you must introduce more Properties in the ViewModel and bind them accordingly. Maybe a ListBox with a flexible number of TextBoxes inside is a solution then, like #Haspemulator already answered.
Binding the data against a collection control
In the TestUserControl I guess you have a control (like a ListView) to show the list of loaded things. So bind that control against the list in the ViewModel with
<ListView ... ItemsSource="{Binding GetAllData}" ... />
First you must understand that Binding means not "read the data and then forget the ViewModel". Instead you bind the View to the ViewModel (and its Properties) as long as the View lasts. From this point of view, AllData is a much better name than GetAllData (thanks #Malcolm O'Hare).
Now in your code, every time the View reads the AllData property, a new DataRepository is created. Because of the Binding, that is not what you want, instead you want to have one instance of DataRepository for the whole lifetime of the View, which is used to read the initial data and can later be used to update the View, if the underlying database changes (maybe with an event).
To enable such a behavior you should change the type of the AllData property to an ObservableCollection, so that the View can automatically update the list if changes occur.
public class TestUserControlViewModel : BaseViewModel
private ObservableCollection<DataItem> allData;
public TestUserControlViewModel() {
IGetTheData src = new DataRepository();
this.allData = new ObservableCollection<DataItem>(src.GetData());
}
public ObservableCollection<DataItem> AllData {
get {
return this.allData;
}
}
public void AddDataItem(DataItem item) {
this.allData.Add(item);
}
}
Now if you call AddDataItem later, the ListView will update itself automatically.
Your Property Name is bad. You should call it AllData, not GetAllData.
Since you are returning a collection, you probably should be using some sort of list control (ListBox, ListView).
In that case you'd be doing
<ListBox ItemsSource="{Binding GetAllData}" />
Guten Abend. :) As it already mentioned, since you're returning the collection, it's better to use a ListBox. The comment about having ObservableCollection as a cache is also absolutely valid. I would add that if you need to have your data editable, you should use TextBox inside the ItemTemplate:
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text={Binding SomeOtherData,Mode=TwoWay} />
</DataTemplate>
</ListBox.ItemTemplate>
In this case if user edits the text in the box, data will be updated in your data object, so that it could be saved in the database later.

Categories

Resources