Creating Binding from Code-Behind not working C# XAML - c#

I have the following code that creates a binding in code-behind. However, it does not seem to work (when the text in PageMarginTextBox is changed, nothing happens, and when the app is loaded, the Padding of newPage is not set to the text of PageMarginTextBox). To make matters worse, no Exceptions are thrown at all. All elements have been defined earlier on.
Binding pageMarginBinding = new Binding
{
Source = PageMarginTextBox,
Path = new PropertyPath("Text"),
};
newPage.SetBinding(ContentControl.PaddingProperty, pageMarginBinding);
//PageMarginTextBox.Text determines the Padding of newPage
How can I fix this? Any solutions would be appreciated. Thanks!

You are trying to Bind PaddingProperty to text. Padding property is of type Thickness and Text property is String.
I am not sure whether you want to bind padding / text, just giving you an idea if you want to bind the Padding.
Binding pageMarginBinding = new Binding
{
Source = PageMarginTextBox,
Path = new PropertyPath("Padding"),
};
newPage.SetBinding(ContentControl.PaddingProperty, pageMarginBinding);
Your problem is because you are trying to assign a string to a Thickness. In XAML the compiler internally translates the string "0,0,2,2" to Thickness object. But in code behind you have to write the code for the conversion yourself.
ThicknessConverter myThicknessConverter = new ThicknessConverter();
PageThickness= (Thickness)myThicknessConverter.ConvertFromString(PageMarginTextBox.Text);
Then you have to bind this to your control. Again this is only half the solution. You need to wire this up with the Binding.
private Thickness _pageThickness;
public Thickness PageThickness
{
get
{
return _pageThickness;
}
set
{
_pageThickness = value;
NotifyPropertyChanged("PageThickness");
}
Then you probably can bind it in XAML

Related

WPF Set a TextBox property in code-behind

Hi I want to set the Text property of a Textbox by code behind. At the moment I do using XAML:
<TextBox x:Name="txtFilter" Text="{Binding FiltroFunzioni, Mode=OneWayToSource}" Grid.Row="0" />
As test I did this:
Binding b = new Binding();
b.Mode = BindingMode.OneWayToSource;
b.Path = new PropertyPath("Text"); //??
b.Source = PageViewModel.FiltroFunzioni;
BindingOperations.SetBinding(txtFilter, TextBlock.TextProperty, b);
The variable "FiltroFunzioni" is a string defined as property:
private string _filtroFunzioni = "";
public string FiltroFunzioni
{
get { return _filtroFunzioni; }
set
{
_filtroFunzioni = value;
RaisePropertyChanged("FiltroFunzioni");
_functionsView.Refresh();
}
}
Basically I dunno what kind of value should I set as PropertyPath. Any ideas?
You don't need the PropertyPath here. If you just remove it, your binding should work.
That being said, you should bind in XAML wherever possible.
If your issue is that changes to FiltroFunzioni don't update your textbox, that's because your binding is specifically declared as OneWayToSource: that means that changing the UI changes the source, but changing the source doesn't change the UI. If that isn't what you want, set the Mode to something else, like "TwoWay" - then changes to the source change the UI, AND changes to the UI change the source.
EDIT:
If you really want to bind from your ViewModel instead of just using XAML, TwoWay binding requires utilizing the Path for some reason, when binding through C#. Either of the following solutions work:
b.Source = FiltroFunzioni;
b.Path = new PropertyPath(".");
b.Source = this;
b.Path = new PropertyPath("FiltroFunzioni");
Note that with TwoWay binding you have to either initialize your FiltroFunzioni by setting the TextBox.Text property in your XAML, or setting FiltroFunzioni after the binding was initialized. Otherwise, WPF will immediately override it from the (by default empty) Text in your TextBox.

Setting up a DependencyProperty in the backing store to a UI via a data template - backing store not updated

I have the following DP in my backing store wrapping the InputEnabled property:
public EnableDisableSetting InputEnabled
{
get { return (EnableDisableSetting)this.GetValue(InputEnabledProperty); }
set { this.SetValue(InputEnabledProperty, value); }
}
public static readonly DependencyProperty InputEnabledProperty = DependencyProperty.Register("InputEnabled", typeof(EnableDisableSetting), typeof(EMSBasicDevice));//, new PropertyMetadata(EnableDisableSettings.Enabled));
I have another object structure which is populated with various parameters that us used to control the rows and columns of a DataGrid:
public class DeviceDisableEnable
{
private EnableDisableSetting _Individual_EnDis;
public EnableDisableSetting Individual_EnDis
{
get { return _Individual_EnDis; }
set { _Individual_EnDis = value; }
}
}
(most of the fields omitted, just showing the relevant one)
At runtime, this structure is populated with values from the backing store:
public void LoadDeviceDisable()
{
DeviceDisableEnable dde;
// Third Line
dde = new DeviceDisableEnable(this);
dde.RowHeight = 21;
dde.DataDescription = this.Inputs[0].Name;
dde.ZoneText = InZoneID.ShortName;
dde.LocationText = Inputs[0].LocationTexts[0];
dde.Individual_EnDis = this.Inputs[0].InputEnabled;
dde.Ind_Enable_Text = this.Inputs[0].InputEnabled.Description;
dde.IsHeader = false;
dde.ShowIndEnable = true;
dde.ShowAllEnable = true;
DeviceDisablesList.Add(dde);
So the value dde.Individual_EnDis gets the backing store value from the DP. This works correctly.
The structure shown is used to build the DataGrid in the UI. Each entry in the structure represents a column in the grid. This is the code that builds the column associated with this entry:
// Add a component for the Enable/Disable property
DataGridTemplateColumn EnableCol = new DataGridTemplateColumn();
Binding IndivBind = new Binding("Individual_EnDis");
IndivBind.Mode = BindingMode.TwoWay;
IndivBind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
IndivBind.Converter = new EMSDevices.EnabledDisabledConverter();
Binding bind3 = new Binding("IsHeader");
bind3.Mode = BindingMode.TwoWay;
bind3.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
FrameworkElementFactory dtContent2 = new FrameworkElementFactory(typeof(EMS_Config_Tool.UIComponents.WPF.DeviceDisableWidget));
ImageTemplate = new DataTemplate();
EnableCol.CellTemplate = ImageTemplate;
EnableCol.Width = new DataGridLength(140);
EnableCol.CanUserSort = false;
ImageTemplate.VisualTree = dtContent2;
dtContent2.SetBinding(EMS_Config_Tool.UIComponents.WPF.DeviceDisableWidget.SetValueProperty, IndivBind);
dtContent2.SetValue(EMS_Config_Tool.UIComponents.WPF.DeviceDisableWidget.TextProperty, Properties.Resources.GroupEditor_Enabled);
dtContent2.SetValue(EMS_Config_Tool.UIComponents.WPF.DeviceDisableWidget.TitleProperty, Properties.Resources.Device_Edit_042);
dtContent2.SetBinding(EMS_Config_Tool.UIComponents.WPF.DeviceDisableWidget.TitleVisibilityProperty, bind3);
EnableCol.CellStyle = visible_style;
TheDataGrid.Columns.Add(EnableCol);
It's a bit complicated, sorry, but there are some funny display needs that go beyond the basic DataGrid, so there are some tricks here to show the right things. The important bit is how the binding is done to the Individual_EnDis property in the DeviceDisableEnable structure.
So far so good, it all woks, at least one way, but sadly not two ways. Changing the value in the UI by clicking the Checkbox does in fact update the setting in the DDE structure --- but --- and here finally is the issue, the backing store does not get updated, so although there is a DP wrapping the property in the backing store, that DP does not "ripple through" the DDE structure to let the backing store be updated by the UI, in other words, using the intermediary structure only presents the value of the property, not the property itself. Clearly I need to change the DDE structure to correctly refer to the backing store DP property, but how to proceed now, I don't know.
Oh dear..... Well, I have answered my own question, clearly I had obfuscated the problem beyond easy view of it's obvious solution.
If the grid is populated from the intermediate data template, changing the data in the backing store will not have any effect on the intermediate structure unless that too is updated. So, all I needed to do was to update the intermediate structure with the new data values, and call [DataGrid].Items.Refresh() and the grid updates to show the new data entries. Of course, this may not be the most efficient as most of the rows don't change in this transaction so what I need to do is only call the Refresh method on the affected entries, I already know which ones they are as I have just updated their values in the intermediate structure.
Most likely, using an iNotifyPropertyChanged notification I can directly get the intermediate structure refreshed when a change occurs in the backing store, then propagate that through to the DataGrid.

Binding in Code

I have a ScatterViewItem, which contains a UserControl. I'm trying to bind the MinWidth of the ScatterViewItem to the UserControl.
ScatterViewItem svi = new ScatterViewItem();
MyUserControl myUserControl = new MyUserControl();
//Make UC follow SVI's size. This code works.
myUserControl.SetBinding(UserControl.WidthProperty, svi.Width.ToString());
myUserControl.SetBinding(UserControl.HeightProperty, svi.Height.ToString());
//Make SVI follow UC's Min size. This doesn't work.
svi.SetBinding(ScatterViewItem.MinWidthProperty, myUserControl.MinWidth.ToString());
svi.SetBinding(ScatterViewItem.MinHeightProperty, myUserControl.MinHeight.ToString());
svi.Content = myUserControl;
myScatterView.Items.Add(svi);
Why is it that binding UC to SVI works and not the other way? How do I bind the MinWidth of the SVI to the UC then?
The SetBinding method has 2 overloads (source: http://msdn.microsoft.com/en-us/library/ms598270(v=vs.110).aspx):
SetBinding(DependencyProperty, String)
SetBinding(DependencyProperty, BindingBase)
What you are trying to achieve by using the ToString() is not working, since the ToString() converts the value of the (Min)Width and (Min)Height properties to a string (for example 500.0 => "500.0"). The SetBinding overload that accepts a string as second parameter expects that string to be a property name or a path to the property.
What you probably want, is "MinWidth", "Width", "MinHeight" or "Height":
myUserControl.SetBinding(UserControl.WidthProperty, "Width");
myUserControl.SetBinding(UserControl.HeightProperty, "Height");
svi.SetBinding(ScatterViewItem.MinWidthProperty, "MinWidth");
svi.SetBinding(ScatterViewItem.MinHeightProperty, "MinHeight");
Edit: this is the correct version, using the other overload, since the previous piece of code doesn't know where to find the specified properties.
Binding widthBinding = new Binding("Width");
widthBinding.Source = myUserControl;
svi.SetBinding(ScatterViewItem.MinWidthProperty, widthBinding);
Binding heightBinding = new Binding("Height");
heightBinding.Source = myUserControl;
svi.SetBinding(ScatterViewItem.MinHeightProperty, heightBinding);
To bind your ScatterViewItem's MinWidth to your usercontrol's MinWidth, you need to create a binding with the source set to the usercontrol and path set to "MinWidth". This binding is then assigned to the ScatterViewItem with SetBinding.
// Create Binding
Binding b = new Binding("MinWidth");
b.Source = myUserControl;
// Assign Binding to ScatterViewItem
svi.SetBinding(ScatterViewItem.MinWidthProperty, b);

Attaching DataTriggers at runtime

Is it possible to attach datatriggers to a style at runtime? I've been through my (non-working) code a few times now and can't seem to find where I've gone wrong.
Here's the method I use to attach the style and trigger:
private void AttachVisibilityTrigger(Control ctrl)
{
Style stl = new System.Windows.Style();
DataTrigger dt = new DataTrigger();
PropertyInfo pi = _entity.GetType().GetProperty(this.SecondaryOptions[ctrl.Name]);
Type controlType = this.GetControlTypeForProperty(ref dt, pi); //gets the control type based on the property name and then sets the value for the DataTrigger for which I want the visibility to be hidden
Binding b = this.GetVisibilityBindingByControlType(controlType); //returns a new Binding with the appropriate Path set that corresponds to the bound property value (e.g IsChecked for CheckBoxes, Text for TextBoxes, SelectedValue for Comboboxes, etc)
b.ElementName = this.SecondaryOptions[ctrl.Name];
dt.Binding = b;
dt.Setters.Add(new Setter(Control.VisibilityProperty, System.Windows.Visibility.Hidden));
stl.Triggers.Add(dt);
ctrl.Style = stl;
}
I'm pretty sure the binding is just broken, i created similar styles in code and they work.
Especially this line looks quite suspicious:
b.ElementName = this.SecondaryOptions[ctrl.Name];
(If you want to bind to the control itself use RelativeSource instead.)
Have you checked the Output window of VisualStudio for binding errors?

Setting WPF text to TextBlock

I know that TextBlock can present a FlowDocument, for example:
<TextBlock Name="txtFont">
<Run Foreground="Maroon" FontFamily="Courier New" FontSize="24">Courier New 24</Run>
</TextBlock>
I would like to know how to set a FlowDocument that is stored in a variable to a TextBlock.
I am looking for something like:
string text = "<Run Foreground="Maroon" FontFamily="Courier New" FontSize="24">Courier New 24</Run>"
txtFont.Text = text;
However, The result of the code above is that the XAML text is presented unparsed.
EDIT: I guess my question was not clear enough. What I'm really trying to achive is:
The user input some text into a RichTextBox.
The application saves the user input as FlowDocument from the RichTextBox, and serializes it to the disk.
The FlowDocument is deserialized from the disk to the variable text.
Now, I would like to be able to present the user text in a TextBlock.
Therefore, as far as I understand, creating a new Run object and setting the parameters manually will not solve my problem.
The problem is that serializing RichTextBox creates Section object, which I cannot add to TextBlock.Inlines. Therefore, it is not possible to set the deserialized object to TextProperty of TextBlock.
create and add the object as below:
Run run = new Run("Courier New 24");
run.Foreground = new SolidColorBrush(Colors.Maroon);
run.FontFamily = new FontFamily("Courier New");
run.FontSize = 24;
txtFont.Inlines.Add(run);
I know that TextBlock can present FlowDocument
What makes you think that ? I don't think it's true... The content of a TextBlock is the Inlines property, which is an InlineCollection. So it can only contain Inlines... But in a FlowDocument, the content is the Blocks property, which contains instances of Block. And a Block is not an Inline
If your FlowDocument has been deserialized, it means that you have an object of type FlowDocument, right? Try setting the Text property of your TextBlock to this value. Of course, you cannot do this with txtFont.Text = ..., since this only works for strings. For other types of objects, you need to set the DependencyProperty directly:
txtFont.SetValue(TextBlock.TextProperty, myFlowDocument)
Here is how we are setting the look of a textblock by assigning a style on-the-fly.
// Set Weight (Property setting is a string like "Bold")
FontWeight thisWeight = (FontWeight)new FontWeightConverter().ConvertFromString(Properties.Settings.Default.DealerMessageFontWeightValue);
// Set Color (Property setting is a string like "Red" or "Black")
SolidColorBrush thisColor = (SolidColorBrush)new BrushConverter().ConvertFromString(Properties.Settings.Default.DealerMessageFontColorValue);
// Set the style for the dealer message
// Font Family Property setting is a string like "Arial"
// Font Size Property setting is an int like 12, a double would also work
Style newStyle = new Style
{
TargetType = typeof(TextBlock),
Setters = {
new Setter
{
Property = Control.FontFamilyProperty,
Value = new FontFamily(Properties.Settings.Default.DealerMessageFontValue)
},
new Setter
{
Property = Control.FontSizeProperty,
Value = Properties.Settings.Default.DealerMessageFontSizeValue
},
new Setter
{
Property = Control.FontWeightProperty,
Value = thisWeight
},
new Setter
{
Property = Control.ForegroundProperty,
Value = thisColor
}
}
};
textBlock_DealerMessage.Style = newStyle;
You can eliminate the style section and set properties directly, but we like keeping things bundled in the style to help us organize the look throughout the project.
textBlock_DealerMessage.FontWeight = thisWeight;
HTH.

Categories

Resources