I'm using Sitecore 8 Update 2 and the WFFM module.
I created a register page using WFFM and everything works fine except for the values in the dropdown lists ( zipcode, city, gender, ... )
I linked all the form fields to the profile fields but these values never get filled in. I tried linking the form dropdown to a dropdown field on the profile, to a simple text field on the profile, but it never fills in any value.
Does anyone know why this is and what i can do to link these fields ?
This is a bug in WFFM. To fix it, add the code below to your solution then go to the item /sitecore/system/Modules/Web Forms for Marketers/Settings/Field Types/List Types/Drop List and set the field MVC Type to {your namespace}.DropListField,[your dll name}
namespace {your namespace}
{
using System.Collections.Generic;
using System.Linq;
using Sitecore.Data.Items;
using Sitecore.Form.Core.Controls.Data;
/// <summary>
/// The drop list field.
/// </summary>
public class DropListField : Sitecore.Forms.Mvc.Models.Fields.DropListField
{
/// <summary>
/// Initialises a new instance of the <see cref="DropListField"/> class.
/// </summary>
/// <param name="item">
/// The item.
/// </param>
public DropListField(Item item)
: base(item)
{
}
/// <summary>
/// The get result.
/// </summary>
/// <returns>
/// The <see cref="ControlResult"/>.
/// </returns>
public override ControlResult GetResult()
{
var value = this.Value as List<string>;
var selectListItem = this.Items.SingleOrDefault(x => x.Value == value.First());
var str = selectListItem != null ? selectListItem.Text : string.Empty;
return new ControlResult(this.ID.ToString(), this.Title, selectListItem != null ? selectListItem.Value : string.Empty, str);
}
}
}
Sounds like it could be this bug:
http://www.cmsbestpractices.com/bug-sitecore-8-with-web-forms-for-marketers-drop-list-entries-not-being-properly-recorded-in-xdb/
Use Robomongo to check if the values are being stored properly in MongoDB.
Related
I have a file named App.xml that is included in my installer(will be installed on the client computer) which I want to load data from and display it to the user so he can manipulate what will be installed/ how to use the system.
I've tried using Xml Files extensions/ custom actions, checked online, couldn't find a way to load a source file embedded in the installer.
my file is:
<App> <Text>bla bla</text></App>
I want the installer to show the "bla bla" text and the user can change it which can later be saved through an extension as usual..
thanks!
I've run into a similar problem. Digging around for a solution that allows you to use an XML file during the InstallUISequence, before files are installed, it looks like you need to add a custom table to your WiX definition as described here.
Basically, you create a CustomTable element in your .wxs file, for your example something like:
<CustomTable Id="App">
<Text>bla bla</Text>
</CustomTable>
Then you can read from it in a c# custom action by creating a View query to find the desired properties:
using (View view = session.Database.OpenView("SELECT 'Text' FROM 'App'"))
{
view.Execute();
// access view properties and turn them into some object you want to manipulate
}
I'll admit some ignorance as to what that view object is going to have, but I know you can iterate through its records or grab individual columns, poking around in the properties should eventually find you the values you want.
Next step is to populate a combobox element with the values
<Control Id="DropdownSelectLabel" Type="Text" X="50" Y="65" Width="200" Height="15" TabSkip="no" Text="&Select a value:">
</Control>
<Control Id="DropdownSelect" Type="ComboBox" Height="16" Width="200" X="60" Y="80" Property="MY_PROPERTY_KEY" ComboList="yes">
<ComboBox Property="MY_PROPERTY_KEY">
<!-- Optional prepopulate value-->
<ListItem Text="[dummy_text]" Value="[dummy_value]" />
</ComboBox>
</Control>
I'm populating it with a custom c# action running during the InstallUISequence, built through visual studio
<!-- Custom action for populating the combobox -->
<CustomAction Id="CA_PopulateComboBox" BinaryKey="BIN_CustomActions" DllEntry="PopulateComboBox" Execute="firstSequence" />
<!-- Binaries for the custom action -->
<Binary Id="BIN_CustomActions" SourceFile="..\PATH-TO-YOUR-CUSTOM-ACTION-BIN-RELEASE.CA.dll" />
<!-- Schedule the custom action -->
<InstallUISequence>
<Custom Action="CA_PopulateComboBox" Before="LaunchConditions" />
</InstallUISequence>
Custom action looks like this:
public class CustomActions
{
/// <summary>
/// Populates the ComboBox UI Element.
/// </summary>
/// <param name="session">The session.</param>
[CustomAction]
public static void PopulateComboBox(Session session)
{
session.Log("Populating the combobox with certificates");
// Clear the combobox (unecessary if it starting empty)
View view = session.Database.OpenView("DELETE FROM ComboBox WHERE ComboBox.Property='MY_PROPERTY_KEY'");
view.Execute();
view = session.Database.OpenView("SELECT * FROM ComboBox");
view.Execute();
List<ComboBoxRecordWrapper> valuesToAdd = PopulateValuesObjects(session); // Add the logic to read your xml values from the session object here
var index = 1;
foreach (ComboBoxRecordWrapper valueObject in valuesToAdd)
{
session.Log($"Adding value to the combobox: {valueObject.Text} - {valueObject.Value} {Environment.NewLine}Order: {valueObject.Order}");
view.Modify(ViewModifyMode.InsertTemporary, recordWrapper.ToRecord());
view.Execute();
index++;
}
view.Close();
}
}
/// <summary>
/// Class ComboBoxRecordWrapper. Wraps objects that should be represented in a combobox element in the installer
/// </summary>
public class ComboBoxRecordWrapper
{
/// <summary>
/// Gets or sets the property that this element's value will be stored as if the element is selected
/// </summary>
/// <value>The property.</value>
public string Property { get; set; }
/// <summary>
/// Gets or sets the order that this element appears in the combobox
/// </summary>
/// <value>The order.</value>
public int Order { get; set; }
/// <summary>
/// Gets or sets the value of the combobox option. This is what will be available to the UI element as a returned value
/// </summary>
/// <value>The value.</value>
public string Value { get; set; }
/// <summary>
/// Gets or sets the text that will be displayed for this element
/// </summary>
/// <value>The text.</value>
public string Text { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ComboBoxRecordWrapper"/> class.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="order">The order.</param>
/// <param name="value">The value.</param>
/// <param name="text">The text.</param>
public ComboBoxRecordWrapper(string property, int order, string value, string text)
{
this.Property = property;
this.Order = order;
this.Value = value;
this.Text = string.IsNullOrEmpty(text) ? value : text;
}
/// <summary>
/// Converts to a record to add to the MSI database.
/// </summary>
/// <returns>Record.</returns>
public Record ToRecord()
{
var record = new Record(4);
record.SetString(1, this.Property);
record.SetInteger(2, this.Order);
record.SetString(3, this.Value);
record.SetString(4, this.Text);
return record;
}
}
This would be custom functionality that you would have to provide through a custom action. From a high level design I see you passing in the path to the xml file on the command line. At run time your custom action would read the nodes in the file and set the appropriate properties that your text boxes are using.
I m hitting the following error on my new windows 10 universal app C#/XAML:
An exception of type 'System.InvalidCastException' occurred in GalaSoft.MvvmLight.Platform.dll but was not handled in user code
Additional information: Unable to cast object of type '' to type 'Windows.UI.Xaml.Controls.Frame'.
on the following navigating command in one of my page's view model:
_navigationService.NavigateTo(ViewModelLocator.MedicineBoxPageKey);
I am trying to have a hamburger menu style navigation (see this sample). app by Microsoft on an example of how to do this) to:
1- have a convenient solution shared across all my pages. The sample mentioned above uses an AppShell Page as the root of the app instead of a Frame, that encapsulates the navigation menu and some behavior of the back button. That would be ideal.
2- Use the MVVM-Light navigation service to handle all the navigation from my view model conveniently.
Here is how the App.xml.Cs initializes the shell page onLaunched:
AppShell shell = Window.Current.Content as AppShell;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (shell == null)
{
// Create a a AppShell to act as the navigation context and navigate to the first page
shell = new AppShell();
// Set the default language
shell.Language = Windows.Globalization.ApplicationLanguages.Languages[0];
shell.AppFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
}
// Place our app shell in the current Window
Window.Current.Content = shell;
if (shell.AppFrame.Content == null)
{
// When the navigation stack isn't restored, navigate to the first page
// suppressing the initial entrance animation.
shell.AppFrame.Navigate(typeof(MedicinesStorePage), e.Arguments, new Windows.UI.Xaml.Media.Animation.SuppressNavigationTransitionInfo());
}
// Ensure the current window is active
Window.Current.Activate();
And here is the AppShell class definition:
public sealed partial class AppShell : Page
{
public static AppShell Current = null;
public AppShell()
{
this.InitializeComponent();
}
}
From what I have tried so far, the mvvm-light navigation service only works when a Frame is used a root of the app and note a Page (otherwise we get this casting bug).
However using a Frame does not seem to be a option either since as the sample app puts it:
Using a Page as the root for the app provides a design time experience as well as ensures that
when it runs on Mobile the app content won't appear under the system's StatusBar which is visible
by default with a transparent background. It will also take into account the presence of software
navigation buttons if they appear on a device. An app can opt-out by switching to UseCoreWindow.
I also tried to overide the navigationTo method from the mvvm-light navigation service but the bug seems to occur before I could catch it.
Does anyone has a solution to use the mvvm-light navigation service and a shell page as the app root (that manages the hamburger menu, etc.)?
Thanks a lot!
I talked to Laurent Bugnion and he recommended me to implemented my own navigation service who handles the navigation. For this I made a PageNavigationService who implements the INavigationService Interface of MVVM Light.
public class PageNavigationService : INavigationService
{
/// <summary>
/// The key that is returned by the <see cref="CurrentPageKey" /> property
/// when the current Page is the root page.
/// </summary>
public const string RootPageKey = "-- ROOT --";
/// <summary>
/// The key that is returned by the <see cref="CurrentPageKey" /> property
/// when the current Page is not found.
/// This can be the case when the navigation wasn't managed by this NavigationService,
/// for example when it is directly triggered in the code behind, and the
/// NavigationService was not configured for this page type.
/// </summary>
public const string UnknownPageKey = "-- UNKNOWN --";
private readonly Dictionary<string, Type> _pagesByKey = new Dictionary<string, Type>();
/// <summary>
/// The key corresponding to the currently displayed page.
/// </summary>
public string CurrentPageKey
{
get
{
lock (_pagesByKey)
{
var frame = ((AppShell) Window.Current.Content).AppFrame;
if (frame.BackStackDepth == 0)
{
return RootPageKey;
}
if (frame.Content == null)
{
return UnknownPageKey;
}
var currentType = frame.Content.GetType();
if (_pagesByKey.All(p => p.Value != currentType))
{
return UnknownPageKey;
}
var item = _pagesByKey.FirstOrDefault(
i => i.Value == currentType);
return item.Key;
}
}
}
/// <summary>
/// If possible, discards the current page and displays the previous page
/// on the navigation stack.
/// </summary>
public void GoBack()
{
var frame = ((Frame) Window.Current.Content);
if (frame.CanGoBack)
{
frame.GoBack();
}
}
/// <summary>
/// Displays a new page corresponding to the given key.
/// Make sure to call the <see cref="Configure" />
/// method first.
/// </summary>
/// <param name="pageKey">
/// The key corresponding to the page
/// that should be displayed.
/// </param>
/// <exception cref="ArgumentException">
/// When this method is called for
/// a key that has not been configured earlier.
/// </exception>
public void NavigateTo(string pageKey)
{
NavigateTo(pageKey, null);
}
/// <summary>
/// Displays a new page corresponding to the given key,
/// and passes a parameter to the new page.
/// Make sure to call the <see cref="Configure" />
/// method first.
/// </summary>
/// <param name="pageKey">
/// The key corresponding to the page
/// that should be displayed.
/// </param>
/// <param name="parameter">
/// The parameter that should be passed
/// to the new page.
/// </param>
/// <exception cref="ArgumentException">
/// When this method is called for
/// a key that has not been configured earlier.
/// </exception>
public void NavigateTo(string pageKey, object parameter)
{
lock (_pagesByKey)
{
if (!_pagesByKey.ContainsKey(pageKey))
{
throw new ArgumentException(
string.Format(
"No such page: {0}. Did you forget to call NavigationService.Configure?",
pageKey),
"pageKey");
}
var shell = ((AppShell) Window.Current.Content);
shell.AppFrame.Navigate(_pagesByKey[pageKey], parameter);
}
}
/// <summary>
/// Adds a key/page pair to the navigation service.
/// </summary>
/// <param name="key">
/// The key that will be used later
/// in the <see cref="NavigateTo(string)" /> or <see cref="NavigateTo(string, object)" /> methods.
/// </param>
/// <param name="pageType">The type of the page corresponding to the key.</param>
public void Configure(string key, Type pageType)
{
lock (_pagesByKey)
{
if (_pagesByKey.ContainsKey(key))
{
throw new ArgumentException("This key is already used: " + key);
}
if (_pagesByKey.Any(p => p.Value == pageType))
{
throw new ArgumentException(
"This type is already configured with key " + _pagesByKey.First(p => p.Value == pageType).Key);
}
_pagesByKey.Add(
key,
pageType);
}
}
}
Basicly it's a copy of his implementation. But instead of parsing to a Frame I parse to an AppShell and use the AppFrame Property to navigate.
I put this to my ViewModelLocator. Instead of:
var navigationService = new NavigationService();
I will just use:
var navigationService = new PageNavigationService();
EDIT: I Noticed that there is an excpetion in the NavMenuListView when you use the backkey after you navigated with the new navigationservice since the selected item is null. I fixed it with adjusting the SetSelectedItem Method and adding a nullcheck in the for loop after the cast:
public void SetSelectedItem(ListViewItem item)
{
var index = -1;
if (item != null)
{
index = IndexFromContainer(item);
}
for (var i = 0; i < Items.Count; i++)
{
var lvi = (ListViewItem) ContainerFromIndex(i);
if(lvi == null) continue;
if (i != index)
{
lvi.IsSelected = false;
}
else if (i == index)
{
lvi.IsSelected = true;
}
}
}
But there might be a more elegant solution than this.
I'm developing Window Forms project with Entity Framework 6.0. I apply Database-first approach and create edmx file by using Visual Studio Wizards. Like all others, I also produce pre-generated view to improve performance. I used Entity Framework Power Tools. It works and produce my_EF.views.cs file.
But my first query still take long time.
I doubt that my project file structure might be the problem. In my solution explorer, I have two projects "MyApp.GUI" (Windows Form Project) and "MyApp.DataAccess" (Class Library Project). I add reference dll of "MyApp.DataAccess" to "MyApp.GUI".
My Entity Framework edmx file is located in "MyApp.DataAccess" project. I don't know where to put my pre-generated view file whether in "MyApp.DataAccess" class library or "MyApp.GUI" windows form project. Currently the pre-generated view file is in "MyApp.DataAccess" class library.
Does my problem associated with my project file structure ? Or, may be the pre-generated view file does not work (produced by Entity Framework Power Tools) ? This is my pre-generated view file. Please suggest me possible solutions.
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System.Data.Entity.Infrastructure.MappingViews;
[assembly: DbMappingViewCacheTypeAttribute(
typeof(TheWayPointOfSale.DataAccess.TheWayPOSEntities),
typeof(Edm_EntityMappingGeneratedViews.ViewsForBaseEntitySetsa6f884d523752b7a79e1bab1a97ba058ffdac6b45a04cc850e30f503a4e1fc49))]
namespace Edm_EntityMappingGeneratedViews
{
using System;
using System.CodeDom.Compiler;
using System.Data.Entity.Core.Metadata.Edm;
/// <summary>
/// Implements a mapping view cache.
/// </summary>
[GeneratedCode("Entity Framework Power Tools", "0.9.0.0")]
internal sealed class ViewsForBaseEntitySetsa6f884d523752b7a79e1bab1a97ba058ffdac6b45a04cc850e30f503a4e1fc49 : DbMappingViewCache
{
/// <summary>
/// Gets a hash value computed over the mapping closure.
/// </summary>
public override string MappingHashValue
{
get { return "a6f884d523752b7a79e1bab1a97ba058ffdac6b45a04cc850e30f503a4e1fc49"; }
}
/// <summary>
/// Gets a view corresponding to the specified extent.
/// </summary>
/// <param name="extent">The extent.</param>
/// <returns>The mapping view, or null if the extent is not associated with a mapping view.</returns>
public override DbMappingView GetView(EntitySetBase extent)
{
if (extent == null)
{
throw new ArgumentNullException("extent");
}
var extentName = extent.EntityContainer.Name + "." + extent.Name;
if (extentName == "TheWayPOSModelStoreContainer.Products")
{
return GetView0();
}
if (extentName == "TheWayPOSModelStoreContainer.Um")
{
return GetView1();
}
if (extentName == "TheWayPOSModelStoreContainer.Products_Um")
{
return GetView2();
}
if (extentName == "TheWayPOSEntities.Products")
{
return GetView3();
}
if (extentName == "TheWayPOSEntities.Ums")
{
return GetView4();
}
if (extentName == "TheWayPOSEntities.Products_Um")
{
return GetView5();
}
return null;
}
/// <summary>
/// Gets the view for TheWayPOSModelStoreContainer.Products.
/// </summary>
/// <returns>The mapping view.</returns>
private static DbMappingView GetView0()
{
return new DbMappingView(#"
SELECT VALUE -- Constructing Products
[TheWayPOSModel.Store.Products](T1.[Products.product_code], T1.[Products.product_name], T1.[Products.buying_price], T1.[Products.discount_percentage], T1.[Products.retail_price], T1.[Products.wholesale_price], T1.Products_supplier, T1.[Products.created_at], T1.[Products.updated_at])
FROM (
SELECT
T.product_code AS [Products.product_code],
T.product_name AS [Products.product_name],
T.buying_price AS [Products.buying_price],
T.discount_percentage AS [Products.discount_percentage],
T.retail_price AS [Products.retail_price],
T.wholesale_price AS [Products.wholesale_price],
T.supplier AS Products_supplier,
T.created_at AS [Products.created_at],
T.updated_at AS [Products.updated_at],
True AS _from0
FROM TheWayPOSEntities.Products AS T
) AS T1");
}
/// <summary>
/// Gets the view for TheWayPOSModelStoreContainer.Um.
/// </summary>
/// <returns>The mapping view.</returns>
private static DbMappingView GetView1()
{
return new DbMappingView(#"
SELECT VALUE -- Constructing Um
[TheWayPOSModel.Store.Um](T1.[Um.um_code], T1.[Um.um_shortname], T1.[Um.um_fullname], T1.Um_disposable, T1.[Um.disposed_um_code], T1.[Um.disposed_um_qty], T1.[Um.depend_on_product], T1.[Um.created_at], T1.[Um.updated_at])
FROM (
SELECT
T.um_code AS [Um.um_code],
T.um_shortname AS [Um.um_shortname],
T.um_fullname AS [Um.um_fullname],
T.disposable AS Um_disposable,
T.disposed_um_code AS [Um.disposed_um_code],
T.disposed_um_qty AS [Um.disposed_um_qty],
T.depend_on_product AS [Um.depend_on_product],
T.created_at AS [Um.created_at],
T.updated_at AS [Um.updated_at],
True AS _from0
FROM TheWayPOSEntities.Ums AS T
) AS T1");
}
/// <summary>
/// Gets the view for TheWayPOSModelStoreContainer.Products_Um.
/// </summary>
/// <returns>The mapping view.</returns>
private static DbMappingView GetView2()
{
return new DbMappingView(#"
SELECT VALUE -- Constructing Products_Um
[TheWayPOSModel.Store.Products_Um](T1.[Products_Um.id], T1.[Products_Um.product_code], T1.[Products_Um.um_code], T1.[Products_Um.disposed_um_code], T1.[Products_Um.disposed_um_qty])
FROM (
SELECT
T.id AS [Products_Um.id],
T.product_code AS [Products_Um.product_code],
T.um_code AS [Products_Um.um_code],
T.disposed_um_code AS [Products_Um.disposed_um_code],
T.disposed_um_qty AS [Products_Um.disposed_um_qty],
True AS _from0
FROM TheWayPOSEntities.Products_Um AS T
) AS T1");
}
/// <summary>
/// Gets the view for TheWayPOSEntities.Products.
/// </summary>
/// <returns>The mapping view.</returns>
private static DbMappingView GetView3()
{
return new DbMappingView(#"
SELECT VALUE -- Constructing Products
[TheWayPOSModel.Product](T1.[Product.product_code], T1.[Product.product_name], T1.[Product.buying_price], T1.[Product.discount_percentage], T1.[Product.retail_price], T1.[Product.wholesale_price], T1.Product_supplier, T1.[Product.created_at], T1.[Product.updated_at])
FROM (
SELECT
T.product_code AS [Product.product_code],
T.product_name AS [Product.product_name],
T.buying_price AS [Product.buying_price],
T.discount_percentage AS [Product.discount_percentage],
T.retail_price AS [Product.retail_price],
T.wholesale_price AS [Product.wholesale_price],
T.supplier AS Product_supplier,
T.created_at AS [Product.created_at],
T.updated_at AS [Product.updated_at],
True AS _from0
FROM TheWayPOSModelStoreContainer.Products AS T
) AS T1");
}
/// <summary>
/// Gets the view for TheWayPOSEntities.Ums.
/// </summary>
/// <returns>The mapping view.</returns>
private static DbMappingView GetView4()
{
return new DbMappingView(#"
SELECT VALUE -- Constructing Ums
[TheWayPOSModel.Um](T1.[Um.um_code], T1.[Um.um_shortname], T1.[Um.um_fullname], T1.Um_disposable, T1.[Um.disposed_um_code], T1.[Um.disposed_um_qty], T1.[Um.depend_on_product], T1.[Um.created_at], T1.[Um.updated_at])
FROM (
SELECT
T.um_code AS [Um.um_code],
T.um_shortname AS [Um.um_shortname],
T.um_fullname AS [Um.um_fullname],
T.disposable AS Um_disposable,
T.disposed_um_code AS [Um.disposed_um_code],
T.disposed_um_qty AS [Um.disposed_um_qty],
T.depend_on_product AS [Um.depend_on_product],
T.created_at AS [Um.created_at],
T.updated_at AS [Um.updated_at],
True AS _from0
FROM TheWayPOSModelStoreContainer.Um AS T
) AS T1");
}
/// <summary>
/// Gets the view for TheWayPOSEntities.Products_Um.
/// </summary>
/// <returns>The mapping view.</returns>
private static DbMappingView GetView5()
{
return new DbMappingView(#"
SELECT VALUE -- Constructing Products_Um
[TheWayPOSModel.Products_Um](T1.[Products_Um.id], T1.[Products_Um.product_code], T1.[Products_Um.um_code], T1.[Products_Um.disposed_um_code], T1.[Products_Um.disposed_um_qty])
FROM (
SELECT
T.id AS [Products_Um.id],
T.product_code AS [Products_Um.product_code],
T.um_code AS [Products_Um.um_code],
T.disposed_um_code AS [Products_Um.disposed_um_code],
T.disposed_um_qty AS [Products_Um.disposed_um_qty],
True AS _from0
FROM TheWayPOSModelStoreContainer.Products_Um AS T
) AS T1");
}
}
}
I know this information is not worth to be an answer. But for other newbies like me, I would like to share my experience.
According to Maarten's comment, I have put breakpoints in each of the pre-generated view files. The pre-generated view in DataAccess class library is hit by the breakpoint. So, it can be generally assume to place the pre-generated view file in DataAccess class library where edmx file is located (in my kinds of situations).
i have program with some user controls but when i build solutions the user controls don't appear on tool box, for test i made another user control and after re build it appeared on tool box ! this is one of my user controls:
using System.Drawing;
using System.Windows.Forms;
namespace AMIgo
{
/// <summary>A UserControl used to display and interact with the active calls.</summary>
/// <remarks>This display reflect the content of the <see cref="AMI_Client.AstCallsList"/> which contains the current active calls.
/// The ListViewCalls display allows to show the items in differents layouts, in a similar way than the windows file explorer.
/// <para>A menu allows to select to display Inbound( show in Red (default), Outbound (shown in blue) and internal (shown in gray) calls.</para>
/// <para>Properties allows to set the font and colors of the ListView and ListItems</para>
/// <para>Drag / Drop: You can drag an active call and drop it onto an extension in the <see cref="ListViewLines"/> display to Transfer a
/// caller to an extension. You can drag and drop an active call to the display toolbar drop target to Transfer the active channel to the
/// selected destination, to park the call or to hang it up. You can also drag an active channel to the <see cref="AMIgoDropTarget"/> form to Transfer a call to the selected destination.
/// (Extension, queue, conference, etc)</para>
/// <para> Context Menu: Right click on an item to open a Context menu.</para>
/// <seealso cref="AstCall"/><seealso cref="AMI_Client.NewCallEvent"/></remarks>
[ToolboxBitmap(typeof(AMIgo.ListViewCalls), "Resources.Control_ListView.bmp")]
public partial class ListViewCalls : AMIgoControl
{
/// <summary>Gets or sets a value indicating whether the intermediary agents channels are displayed.</summary>
/// <value><c>true</c> to show Agents channels otherwise, <c>false</c>.</value>
public bool ShowAgentsChannels { get; set; }
private bool _show_outbound_calls;
/// <summary>Gets or sets a value indicating whether the outbound calls are displayed.</summary>
/// <value><c>true</c> to show outbound calls, otherwise, <c>false</c>.</value>
public bool ShowOutboundCalls
{
get { return (_show_outbound_calls); }
set
{
_show_outbound_calls = value;
outboundToolStripMenuItem.Checked = _show_outbound_calls;
}
}
private bool _show_internal_calls;
.
.
.
.
.
and this is AMIgoControl file...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
namespace AMIgo
{
/// <summary>
/// This is the base control from which inherit most of the AMIgo controls
/// </summary>
[DesignTimeVisible(false)]
[System.ComponentModel.ToolboxItem(false)]
public class AMIgoControl : UserControl
{
/// <summary>true after this control has been initialized. </summary>
public bool _InitDone;
/// <summary>Gets or sets the AMI_Client instance. The AMI_Client component will set this on startup if left blank.</summary>
public virtual AMIgo.AMI_Client AMI_ClientInstance { get; set; }
/// <summary>Initializes a new instance of the <see cref="AMIgoControl"/> class.</summary>
public AMIgoControl() { }
/// <summary>Initializes a new instance of the <see cref="AMIgoControl"/> class. </summary>
/// <param name="AMI_ClientInstance">The AMI_Client instance.</param>
public AMIgoControl(AMIgo.AMI_Client AMI_ClientInstance)
{
this.AMI_ClientInstance = AMI_ClientInstance;
}
/// <summary>Finds a Control recursively. Note finds the first match and exits</summary>
/// <param name="Container">The container to search for the given control type.
/// Remember all controls (Panel, GroupBox, Form, etc are all containers for controls
/// </param>
/// <param name="TypeName">Name of the type of control to look for</param>
/// <returns>The control object if found or null</returns>
///
public Control FindControlRecursive(Control Container, string TypeName)
{
if (Container.GetType().Name == TypeName)
return Container;
foreach (Control ctrl in Container.Controls)
{
Control foundCtrl = FindControlRecursive(ctrl, TypeName);
if (foundCtrl != null)
return foundCtrl;
}
return null;
}
} //UserControl
Just for the starters: Toolbox in VS applies context filters so, you won't see the user controls unless you have a designer opened and active in the document well. If you have tried this and still don't see the controls - would you be able to post a screenshot for better understanding? Also, mention the version of VS you are using - I just tried creating a sample UC on VS12 and it works just as expected. Perhaps, you might want to do that test too and post the update with result.
update: by the way, you might want to remove that ToolboxItem(false) attribute on AMIgo class.
I'm trying to localise a WinForms app for multiple languages. I'm trying to find a way to set my form labels/buttons text properties to read from the resources file in the designer (rather than having to maintain a chunk of code that sets them programatically).
I've found I can set form.Localizable=true, but then the resources are read from a file alongside the form, but many of mine are shared across multiple forms.
Is there any way to set a label's text in the designer, to a value stored in a project-level resx file?
I think I found a way to do this!
First in your Resources.resx set the Access Modifier to Public.
After that in the designer generated code (Form.Designer.cs) you can write this to the appropriate control:
this.<control>.Text = Properties.Resources.<stringname>
for example:
this.footerLabel.Text = Properties.Resources.footerString;
ps.:I don't know how ethical this solution is, but it works!
To answer the question, no.
But IMO, this should not be done anyways if the text will be static.
Have a read at my answers on localization and resources:
Resource string location
Globalize an existing Windows Forms application
Using .resx files for global application messages
Easy enough to implement, by the way, this can be done for any type of control you like to bind to a resource, or any other class. I do this for static classes like my application settings as well.
Entering code like this:
textBox2.DataBindings.Add("Text", source, "<className>.<PropertyName>");
is not giving me a "good feeling", never mind the spelling
Here is a litle sample of the above label that provides a dropdown on the resources of a application.
First the control, contains 1 new property named ResourceName
the magic comes from the editor, this one is specified in the annotation above the property and is called ResourceDropDownListPropertyEditor
[Editor(typeof(ResourceDropDownListPropertyEditor), typeof(System.Drawing.Design.UITypeEditor))]
The code for the label class:
/// <summary>
/// Label bound to resource
/// </summary>
/// <remarks>
/// The bitmap does not appear in the Toolbox for autogenerated controls and components.
/// https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-provide-a-toolbox-bitmap-for-a-control</remarks>
/// <seealso cref="System.Windows.Forms.Label" />
[ToolboxBitmap(typeof(Label))]
public partial class ResourceLabel : Label
{
/// <summary>
/// backing field for the resource key property
/// </summary>
private string mResourceName;
[Browsable(true)]
[DefaultValue("")]
[SettingsBindable(true)]
[Editor(typeof(ResourceDropDownListPropertyEditor), typeof(System.Drawing.Design.UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Description("Select the resource key that you would like to bind the text to.")]
public string ResourceName
{
get { return mResourceName; }
set
{
mResourceName = value;
if (!string.IsNullOrEmpty(mResourceName))
{
base.Text = Properties.Resources.ResourceManager.GetString(mResourceName);
}
}
}
/// <summary>
/// Designer helper method: https://msdn.microsoft.com/en-us/library/ms973818.aspx
/// </summary>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
private bool ShouldSerializeResourceName()
{
return !string.IsNullOrEmpty(ResourceName);
}
/// <summary>
/// Will be default text if no resource is available
/// </summary>
[Description("default text if no resource is assigned or key is available in the runtime language")]
public override string Text
{
get { return base.Text; }
set
{
// Set is done by resource name.
}
}
}
Here is the class used for the drop down:
/// <summary>
/// used for editor definition on those properties that should be able
/// to select a resource
/// </summary>
/// <seealso cref="System.Drawing.Design.UITypeEditor" />
class ResourceDropDownListPropertyEditor : UITypeEditor
{
IWindowsFormsEditorService _service;
/// <summary>
/// Gets the editing style of the <see cref="EditValue"/> method.
/// </summary>
/// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
/// <returns>Returns the DropDown style, since this editor uses a drop down list.</returns>
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
// We're using a drop down style UITypeEditor.
return UITypeEditorEditStyle.DropDown;
}
/// <summary>
/// Displays a list of available values for the specified component than sets the value.
/// </summary>
/// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
/// <param name="provider">A service provider object through which editing services may be obtained.</param>
/// <param name="value">An instance of the value being edited.</param>
/// <returns>The new value of the object. If the value of the object hasn't changed, this method should return the same object it was passed.</returns>
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (provider != null)
{
// This service is in charge of popping our ListBox.
_service = ((IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)));
if (_service != null)
{
var items = typeof(Properties.Resources).GetProperties()
.Where(p => p.PropertyType == typeof(string))
.Select(s => s.Name)
.OrderBy(o => o);
var list = new ListBox();
list.Click += ListBox_Click;
foreach (string item in items)
{
list.Items.Add(item);
}
if (value != null)
{
list.SelectedValue = value;
}
// Drop the list control.
_service.DropDownControl(list);
if (list.SelectedItem != null && list.SelectedIndices.Count == 1)
{
list.SelectedItem = list.SelectedItem.ToString();
value = list.SelectedItem.ToString();
}
list.Click -= ListBox_Click;
}
}
return value;
}
private void ListBox_Click(object sender, System.EventArgs e)
{
if (_service != null)
_service.CloseDropDown();
}
}
In the end what you get will look like this at design-time:
The resource names are created when you drop the control on your form, changes are not seen till you re-compile and close/open the form or drop a new label on the form.
The only way I can think of would be to create a custom control that would add a property for the resource name. When the property is set, grab the value from the project resource file and set the text property with it. You will want to make sure that Text doesn't get serialized or it might overwrite the value set by ResourceName.
public class ResourceLabel
: Label
{
private string mResourceName;
public string ResourceName
{
get { return mResourceName; }
set
{
mResourceName = value;
if (!string.IsNullOrEmpty(mResourceName))
base.Text = Properties.Resources.ResourceManager.GetString(mResourceName);
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override string Text
{
get { return base.Text; }
set
{
// Set is done by resource name.
}
}
}
I have just been looking at this very thing.
If you own the control, ie it is your own custom control, you can use CodeDOM
Read this article for some background and download this example to see how it's done.
In our app we need to replace placeholders with "DisplayText" form the database.
So we have Text properties like "Order {Product}" and we want to replace with GetDisplayText("Order {Product}")`.
So in order to do this I have added the following code:
statements.OfType<CodeAssignStatement>()
.Where(s => s.Left is CodePropertyReferenceExpression && ((CodePropertyReferenceExpression)s.Left).PropertyName == "Text")
.ToList().ForEach(s =>
{
s.Right = new CodeMethodInvokeExpression(
new CodeMethodReferenceExpression(new CodeTypeReferenceExpression("Core.DisplayText"), "GetDisplayText"),
s.Right);
});
However I am still experimenting with it and I haven't created a working solution yet... But it may help you.
:-)