WPF - Access static class in different Namespace - wpf

I have the config for my application in a static class:
namespace Program.Config
{
static Class AppConfig
{
...
}
}
Now, in my xaml I would like to access this config.
So, I added
xmlns:config="clr-namespace:Program.Config"
to my view which resides in Program.Views
However, accessing the AppConfig like
config:AppConfig.ConnectionConfig.conParam.ethPort
does not work.
What is the correct way to access members of a static class from a different namespace in WPF?
EDIT:
Ok, here comes an MWE:
MainWindow.xaml.cs:
using System.Threading;
using System.Windows;
using Program.ViewModels;
namespace Program
{
public partial class MainWindow : Window
{
public MainWindow()
{
MainVM vm = new MainVM();
DataContext = vm;
InitializeComponent();
}
}
}
`MainVM.cs
using Prism.Mvvm;
using System;
namespace Program.ViewModels
{
internal class MainVM : BindableBase
{
public MainVM()
{
}
}
}
`MainWindow.xaml
<Window x:Class="Program.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:config="clr-namespace:Program.Config"
mc:Ignorable="d"
Title="MainWindow" Height="700" Width="1000">
<DockPanel LastChildFill="False">
<Label Content="Port is:" />
<Label Content="{Binding config:AppConfig.ConnectionConfig.conParam.ethPort}" />
</DockPanel>
</Window>
Config.cs:
namespace Program
{
using Program.Config;
public struct ConnectionParameters
{
public int ethPort { get; set; }
public string ethIp { get; set; }
}
public static class AppConfig
{
public static ConnectionConfObj ConnectionConfig { get; set; }
static AppConfig()
{
ConnectionConfig = new ConnectionConfObj();
}
}
}
namespace Program.Config
{
public abstract class ConfigBase
{
public string filepath { get; set; }
}
public class ConnectionConfObj : ConfigBase
{
public ConnectionParameters conParam { get; set; }
public ConnectionConfObj()
{
ConnectionParameters _conParam = new ConnectionParameters();
_conParam.ethPort = 8;
conParam = _conParam;
}
}
}
In the scope of the program, the structure of the config makes sense like this, since I read multiple configs from different sources and want to present them in one AppConfig for the program.

To access a static class member (including enum values) in your XAML, use the x:Static markup extension as follows: {x:Static nmspc:ClassName.MemberName} (or {x:Static nmspc:EnumTypeName.EnumValueName} -- enum values are really static members of a static class). You get a namespace prefix, one class name optionally preceded by intermediate namespaces, and one member name, and that's all it does. If you need a property of MemberName, make the static reference the Source of a Binding. A Binding can have a path to a property of a property:
<Label
Content="{Binding conParam.ethPort, Source={x:Static config:AppConfig.ConnectionConfig)}"
/>

Related

Using RegularExpression as attribute in .NET

I am testing use of regular expression as attribute in my application, but it is simply not working.
public partial class MainWindow : Window
{
[Required]
[RegularExpression(#"^[\d]+")]
public string number { get; set; }
public MainWindow()
{
InitializeComponent();
number = "sometext";
}
}
No error is being thrown and number accepts anything without caring for RegularExpression attribute.
How can I make number only to accept what is mentioned in regex? Usually I do validate in setter, but have learnt recently about attribute and wish to use it.
Thanks.
Your binding source must implement the IDataErrorInfo interface. Then you can set the ValidatesOnDataErrors and NotifyOnValidationError propeties on the binding.
See a simplified example below.
A base class to handle property changes and validation.
internal abstract class ValidatedObservableBase : INotifyPropertyChanged, IDataErrorInfo
{
public event PropertyChangedEventHandler PropertyChanged;
public string this[string columnName]
{
get
{
var results = new List<ValidationResult>();
var valid = Validator.TryValidateProperty(GetType().GetProperty(columnName)?.GetValue(this), new ValidationContext(this) { MemberName = columnName }, results);
return valid ? null : results[0].ErrorMessage;
}
}
public string Error
{
get => null;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The model, derived from the above base class.
internal class Model : ValidatedObservableBase
{
private string number;
[Required(ErrorMessage = "Required error")]
[RegularExpression(#"^[\d]+", ErrorMessage = "Regex error")]
public string Number
{
get => number;
set
{
number = value;
OnPropertyChanged();
}
}
}
A simple view model to set as the window's DataContext.
internal class ViewModel
{
public Model Model { get; set; } = new Model();
}
Lastly, the window.
<Window
...
xmlns:local="clr-namespace:Demo"
mc:Ignorable="d">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<StackPanel>
<TextBox
x:Name="TB"
Margin="24,24,24,0"
VerticalAlignment="Top"
Text="{Binding Path=Model.Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" />
<TextBlock
Margin="24,4,24,24"
Foreground="Red"
Text="{Binding ElementName=TB, Path=(Validation.Errors)[0].ErrorContent}" />
</StackPanel>
</Window>
Thanks for comments. I modified code with some information on this site. myTextbox is bind with number and am using validation attribute. But still this is accepting everything that I write in my textbox.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
myTextBox.DataContext = this;
}
[Required]
[AcceptNumberAttribute]
public string number { get; set; }
}
public sealed class AcceptNumberAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
return new RegularExpressionAttribute(#"^[\d]$").IsValid(Convert.ToString(value).Trim());
}
}

How can i call a Method/value in Resource Dictionary file from App.xaml.cs

I have some .xaml file with TextBlock Control
<TextBlock Text="........." Name="myTxt" />
And I need on Text Property set value "Something" from App.xaml.cs
public partial class App : Application
{
public static string something { get; set; }
}
This should work:
<TextBlock xmlns:local="clr-namespace:WpfApp1" Text="{x:Static local:App.something}" Name="myTxt" />
...provided that you change "WpfApp1" to whatever the namespace of your App class is:
namespace WpfApp1
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public static string something { get; set; } = "something...";
}
}

How to use Message.Attach to call methods inside another object

I have a view model which has another class set as its property. The another class contains implementations of ICommand as its properties. I would like to execute one of the commands on a double click.
Unfortunatelly, Caliburn.Micro raises an exception instead ("No target found for method Commands.Command.Execute.").
I've tried to search the net and read the documentation, but without any success.
How to do it correctly?
Note: In real application, the message might be attached to a grid view which might have a different DataContext than the view model containing the commands class.
The XAML:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org">
<Grid>
<TextBox
cal:Message.Attach="[Event MouseDoubleClick]
= [Action Commands.Command.Execute(null)]" />
</Grid>
</Window>
The code-behind class:
namespace WpfApplication8
{
public partial class MainWindow
{
public Commands Commands { get; set; }
public MainWindow()
{
this.Commands =
new Commands { Command = new Command { MainWindow = this } };
this.InitializeComponent();
this.DataContext = this;
}
}
public class Commands
{
public Command Command { get; set; }
}
public class Command
{
public MainWindow MainWindow { get; set; }
public void Execute(object parameter)
{
this.MainWindow.Title = "Executed";
}
}
}
As #alik commented, complete solution is:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org">
<Grid>
<TextBox
cal:Message.Attach="[Event MouseDoubleClick] = [Action Execute(null)]"
cal:Action.TargetWithoutContext="{Binding Commands.Command}"/>
</Grid>
</Window>

resolve specific dependency in unity DI/IOC

While exploring DI/IOC with Unity with WPF, I came across a question and need your feedback. Please consider the following scenario...
================================================================
public interface IDataServices
{
string GetData();
}
================================================================
public class CopyTextDataServices : IDataServices
{
public string GetData()
{
return "copy text from CopyTextDataServices";
}
}
================================================================
public class TextDataServices : IDataServices
{
public string GetData()
{
return "I am injected by setter property injection";
}
}
================================================================
public interface ITextViewModel
{
string LabelContnet { get; set; }
}
================================================================
public class TextViewModel : ITextViewModel
{
public TextViewModel()
{
LabelContnet = "This is from view model";
}
public string LabelContnet { get; set; }
}
================================================================
public partial class MainWindow : Window
{
public MainWindow(ITextViewModel textViewModel)
{
InitializeComponent();
Loaded += MainWindow_Loaded;
DataContext = textViewModel;
}
[Dependency]
public IDataServices Services { get; set; }
containing the event data.</param>
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
LabelLeft.Content = Services.GetData();
}
}
================================================================
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IUnityContainer container = new UnityContainer();
container.RegisterType<IDataServices, TextDataServices>();
container.RegisterType<IDataServices, CopyTextDataServices>();
container.RegisterType<ITextViewModel, TextViewModel>();
var window = container.Resolve<MainWindow>();
window.Show();
}
}
================================================================
<Window x:Class="TestAppWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" FontSize="20">
<StackPanel>
<Label Content="{Binding Path=LabelContnet,FallbackValue=Left}" HorizontalAlignment="Left" Name="LabelLeft" />
<Label Content="{Binding Path=LabelContnet,FallbackValue=Right}" HorizontalAlignment="Left" Name="LabelRight" />
</StackPanel>
</Window>
===================================================================
Now the result of this appears in the labels is
copy text from CopyTextDataServices
This is from view model
But I want to know if I want to get data from TextDataServices, how can I do that?
The problem is in this line:
container.RegisterType<IDataServices, TextDataServices>();
// This overwrites the previous mapping.
// All dependencies to IDataServices will use CopyTextDataServices.
container.RegisterType<IDataServices, CopyTextDataServices>();
If you want to have both IDataServices, you'll need to register one or both as named instances.
container.RegisterType<IDataServices, TextDataServices>("TextDataServicesName");
container.RegisterType<IDataServices, CopyTextDataServices>("CopyTextDataServicesName");
In your control:
[Dependency("TextDataServicesName")]
public IDataServices Services { get; set; }

WPF DataContext updated but UI not updated

I have maximum simple app. I want to fill listbox when button pressed. I use binding, window DataContext is updated after some operation, but UI not updated!
Here is the code:
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Button" HorizontalAlignment="Left" Margin="432,288.04,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
<ListBox x:Name="urlsListBox" ItemsSource="{Binding Urls}" HorizontalAlignment="Left" Height="300" Margin="10,10,0,0" VerticalAlignment="Top" Width="417"/>
</Grid>
MainWindow.xaml.cs
namespace WpfApplication1
{
public partial class MainWindow : Window
{
ViewModel model = new ViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = model;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
model.GetUrls();
}
}
}
ViewModel.cs
namespace WpfApplication1
{
class ViewModel
{
private ObservableCollection<Url> Urls { get; set; }
public ViewModel()
{
Urls = new ObservableCollection<Url>();
}
public void GetUrls()
{
for (int i = 0; i < 5; i++)
{
Urls.Add(new Url { link = i.ToString() });
}
}
}
public class Url
{
public string link { get; set; }
}
}
Problem stems from the Urls property within the ViewModel class. You need to make Urls public, otherwise the MainWindow cannot access the property:
ViewModel:
namespace WpfApplication1
{
public class ViewModel
{
//make this public otherwise MainWindow will not have access to it!
public ObservableCollection<Url> Urls { get; set; }
public ViewModel()
{
Urls = new ObservableCollection<Url>();
}
public void GetUrls()
{
for (int i = 0; i < 5; i++)
{
Urls.Add(new Url { link = i.ToString() });
}
}
}
public class Url
{
public string link { get; set; }
}
}
Hope this helps and let me know if you have any questions!
You need to support property change notification. Use the NuGet package manager to reference the MVVM Lite project, derive your ViewModel from ViewModelBase and then change your link property to this:
private string _link;
{
public string link
{
get {return this._link;}
set {this._link=value; RaisePropertyChanged(() => this.link); }
}
}
You'll also need to do this for your URLs property which needs to be public in order for binding to work. Also I know this is a little bit outside the scope of the question but in general you shouldn't use the Click handler like this, the "proper" way is to add a RelayCommand to your ViewModel and bind your button's Command property to that.

Resources