在WP7下自定义RelativeSource 的Binding
最近老没有时间上来写博文。今天有空来写写上次还没有说完的话题。上一篇提到说说我在WP7应用开发中遇到的 子控件 DataTemplete 中的按钮的命令绑定,刚开始接触似乎是个头疼的问题。那怎么解决呢?
我们仔细想想 silverlight 就是一个庞大的Composite组合模式的实现。包括了我们所说的可视树。不管怎么加Templete,怎么绑定,最终会出现在可视树上。 好就这么探索去吧。。
你会发现Silverlight, WP7中有类似功能的一些绑定中的 RelativeSource。但好像只是预留的接口,有些没有实现。
首先,观看 silverlight4中有未实现完整的相对绑定。
那我们自己来做吧!我们一些基础建设也抄他们的,呵呵
首先定义一些基础枚举:
public enum RelativeSourceMode{ ParentDataContext = 0, FindAncestor = 1 }}
肯定需要一些相当绑定数据信息,同样参考一下Silverlight4
绑定可能会在同一个控件绑定几个事件,属性所以需要一个集合来管理吧,好做一个简单的。
怎么能给控件一个绑定自定义绑定属性呢? Attache 附加属性 是天生的绝配啊, 给注册一个附加Binding属性 让他可以绑定一个类
,所以做一个抽象的先让数据信息和集合都需继承,这样才能都实用啊。而集合又不需要其他的属性继承下得来,没有意义。 干脆搞个空类:
public abstract class RelativeSourceBase {protected RelativeSourceBase() { } }
参考一下Silverlight4实现相当绑定数据收集工作。
public class RelativeSourceBinding : RelativeSourceBase { /// <summary>/// Gets or sets the path to the binding source property./// </summary> public string Path { get; set; } /// <summary>/// Gets or sets the name of the target dependency property./// </summary> public string TargetProperty { get; set; } /// <summary>/// Gets or sets the XAML namespace. This namespace is used to get the class of an attached dependency property./// </summary> public string TargetNamespace { get; set; } /// <summary>/// Gets or sets the type of ancestor to look for. /// Define the full name (namespace and class name). Xaml Namespace do not work here./// Example: MyNamespace.MyUserControl or System.Windows.ListBox./// For types in System.Windows.dll you can just use the class name instead of the full name./// </summary> public string AncestorType { get; set; } // not implemented yet//public int AncestorLevel { get; set; } /// <summary>/// Gets or sets a value that describes the location of the binding source relative/// to the position of the binding target./// </summary> public RelativeSourceMode RelativeMode { get; set; } /// <summary>/// Gets or sets a value that indicates the direction of the data flow in the binding./// </summary> public BindingMode BindingMode { get; set; } /// <summary>/// Gets or sets the converter object that is called by the binding engine to /// modify the data as it is passed between the source and target, or vice versa./// </summary> public IValueConverter Converter { get; set; } /// <summary>/// Gets or sets the culture to be used by the System.Windows.Data.Binding.Converter./// </summary> public CultureInfo ConverterCulture { get; set; } /// <summary>/// Gets or sets a parameter that can be used in the System.Windows.Data.Binding.Converter logic./// </summary> public object ConverterParameter { get; set; } /// <summary>/// Gets or sets a value that indicates whether the System.Windows.FrameworkElement.BindingValidationError event is raised on validation errors./// </summary> public bool NotifyOnValidationError { get; set; } /// <summary>/// Gets or sets a value that indicates whether the binding engine will report/// validation errors from an System.ComponentModel.IDataErrorInfo implementation/// on the bound data entity./// </summary> public bool ValidatesOnDataErrors { get; set; } /// <summary>/// Gets or sets a value that indicates whether the binding engine will report /// exception validation errors./// </summary> public bool ValidatesOnExceptions { get; set; } /// <summary>/// Gets or sets a value that indicates whether the binding engine will report/// validation errors from an System.ComponentModel.INotifyDataErrorInfo implementation/// on the bound data entity./// </summary> public bool ValidatesOnNotifyDataErrors { get; set; } internal DependencyProperty TargetDependencyProperty { get; set; } }
在搞个List来管理多个绑定,让其也继承基类
public class BindingList : RelativeSourceBase, IList<RelativeSourceBinding>, ICollection<RelativeSourceBinding>, IEnumerable<RelativeSourceBinding>, IList, ICollection, IEnumerable { private List<RelativeSourceBinding> _internalList; public BindingList() { _internalList = new List<RelativeSourceBinding>(); } #region IList<RelativeSourceBinding> Members public int IndexOf(RelativeSourceBinding item) {return _internalList.IndexOf(item); } public void Insert(int index, RelativeSourceBinding item) { _internalList.Insert(index, item); } public void RemoveAt(int index) { _internalList.RemoveAt(index); } public RelativeSourceBinding this[int index] {get {return _internalList[index]; }set { _internalList[index] = value; ; } } #endregion #region ICollection<RelativeSourceBinding> Members public void Add(RelativeSourceBinding item) { _internalList.IndexOf(item); } public void Clear() { _internalList.Clear(); } public bool Contains(RelativeSourceBinding item) {return _internalList.Contains(item); } public void CopyTo(RelativeSourceBinding[] array, int arrayIndex) { _internalList.CopyTo(array, arrayIndex); } public int Count {get { return _internalList.Count; } } public bool IsReadOnly {get { return false; } } public bool Remove(RelativeSourceBinding item) {return _internalList.Remove(item); } #endregion #region IEnumerable<RelativeSourceBinding> Members public IEnumerator<RelativeSourceBinding> GetEnumerator() {return _internalList.GetEnumerator(); } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {return _internalList.GetEnumerator(); } #endregion #region IList Members int IList.Add(object value) { _internalList.Add((RelativeSourceBinding)value);return (this.Count - 1); } void IList.Clear() { _internalList.Clear(); } bool IList.Contains(object value) {return _internalList.Contains((RelativeSourceBinding)value); } int IList.IndexOf(object value) {return _internalList.IndexOf((RelativeSourceBinding)value); } void IList.Insert(int index, object value) { _internalList.Insert(index, (RelativeSourceBinding)value); } bool IList.IsFixedSize {get { return false; } } bool IList.IsReadOnly {get { return false; } } void IList.Remove(object value) { _internalList.Remove((RelativeSourceBinding)value); } void IList.RemoveAt(int index) { _internalList.RemoveAt(index); } object IList.this[int index] {get {return _internalList[index]; }set { _internalList[index] = (RelativeSourceBinding)value; } } #endregion #region ICollection Members void ICollection.CopyTo(Array array, int index) {throw new NotImplementedException(); } int ICollection.Count {get { return _internalList.Count; } } bool ICollection.IsSynchronized {get { return false; } } object ICollection.SyncRoot {get { return _internalList; } } #endregion }
数据搞定了,现在主攻逻辑实现了。。
1.实现附加属性。
2.分析附加属性绑定配置数据信息。
3.查找可视树找到对应UI元素
4.创建绑定。这是时候遇到了问题,需要多重"."属性分析。 还有绑定目标中是否通过名称怎么分析Type? 还好查资料有很多类型的分析代码。
[ContentProperty("Binding")]public static class BindingAdapter { #region Binding (Attached DependencyProperty) public static RelativeSourceBase GetBinding(DependencyObject obj) {return (RelativeSourceBase)obj.GetValue(BindingProperty); } public static void SetBinding(DependencyObject obj, RelativeSourceBase value) { obj.SetValue(BindingProperty, value); } public static readonly DependencyProperty BindingProperty = DependencyProperty.RegisterAttached("Binding", typeof(RelativeSourceBase), typeof(BindingAdapter), new PropertyMetadata(null, OnBinding)); private static void OnBinding(DependencyObject depObj, DependencyPropertyChangedEventArgs e) { FrameworkElement targetElement = depObj as FrameworkElement; if (targetElement != null) {// attach loading event targetElement.Loaded += new RoutedEventHandler(targetElement_Loaded); targetElement.Unloaded += new RoutedEventHandler(targetElement_Unloaded); } } #endregion #region Private methods private static void targetElement_Loaded(object sender, RoutedEventArgs e) {try { FrameworkElement targetElement = sender as FrameworkElement; // release handler to prevent memory leaks targetElement.Loaded -= new RoutedEventHandler(targetElement_Loaded); RelativeSourceBase bindings = GetBinding(targetElement); if (bindings is RelativeSourceBinding) {// get the binding configuration RelativeSourceBinding bindingConfiguration = bindings as RelativeSourceBinding; ProcessBinding(targetElement, bindingConfiguration); }else if (bindings is BindingList) {// get the binding configuration BindingList list = bindings as BindingList; foreach (RelativeSourceBinding bindingConfiguration in list) { ProcessBinding(targetElement, bindingConfiguration); } } }catch (Exception ex) {// ignore this exception, because the SL binding engine does not throw exceptions when a binding is wrong. } } static void targetElement_Unloaded(object sender, RoutedEventArgs e) {try { FrameworkElement targetElement = sender as FrameworkElement; targetElement.Unloaded -= new RoutedEventHandler(targetElement_Unloaded); RelativeSourceBase bindings = GetBinding(targetElement); if (bindings is RelativeSourceBinding) {// get the binding configuration RelativeSourceBinding bindingConfiguration = bindings as RelativeSourceBinding;if (bindingConfiguration.TargetDependencyProperty != null) targetElement.ClearValue(bindingConfiguration.TargetDependencyProperty); }else if (bindings is BindingList) {// get the binding configuration BindingList list = bindings as BindingList; foreach (RelativeSourceBinding bindingConfiguration in list) {if (bindingConfiguration.TargetDependencyProperty != null) targetElement.ClearValue(bindingConfiguration.TargetDependencyProperty); } } }catch { } } private static void ProcessBinding(FrameworkElement targetElement, RelativeSourceBinding bindingConfiguration) { if (bindingConfiguration.RelativeMode == RelativeSourceMode.FindAncestor &&!string.IsNullOrEmpty(bindingConfiguration.AncestorType)) {// navigate up the tree to find the type DependencyObject currentObject = VisualTreeHelper.GetParent(targetElement); DependencyObject candidate = null; DependencyObject ancestor = null; while (true) {if (currentObject == null) {break; } Type currentType = currentObject.GetType(); while (currentType != null && currentType.IsSubclassOf(typeof(DependencyObject))) {if (currentType.FullName == bindingConfiguration.AncestorType) { ancestor = currentObject;break; } // for types in assemblies System.Windows, System.Windows.Controls, System.Windows.Controls.Data, etc, // its possible to define just the class name instead of the full class name including the namespace. if (candidate == null && currentType.Name == bindingConfiguration.AncestorType && currentType.Assembly.FullName.StartsWith("System.Windows")) {// the name of the element is matching, but it is not the fullname.// remeber the element in case if no element is matching to the ancestor type name candidate = currentObject; } // next type up the hierarchy currentType = currentType.BaseType; } // next parent currentObject = VisualTreeHelper.GetParent(currentObject); } // concrete if (ancestor == null) { ancestor = candidate; } if (ancestor != null && ancestor is FrameworkElement) {// bind them CreateBinding(targetElement, ancestor, bindingConfiguration); } }else if (bindingConfiguration.RelativeMode == RelativeSourceMode.ParentDataContext) {object currentDataContext = targetElement.DataContext; // navigate up the tree to find the parent datacontext DependencyObject currentObject = VisualTreeHelper.GetParent(targetElement); while (true) {if (currentObject == null)break; FrameworkElement fe = currentObject as FrameworkElement; if (fe != null) {if (fe.DataContext != null && fe.DataContext != currentDataContext) {// bind them CreateBinding(targetElement, fe.DataContext, bindingConfiguration);break; } } // next parent currentObject = VisualTreeHelper.GetParent(currentObject); } } } private static List<string> GetClassNames(Type type) { List<string> result = new List<string>(); // check if (type == null && type.IsSubclassOf(typeof(DependencyObject)))return result; // process do { result.Add(type.FullName); type = type.BaseType; } while (type != null && type.IsSubclassOf(typeof(DependencyObject))); // return return result; } private static void CreateBinding(FrameworkElement targetElement, object sourceElement, RelativeSourceBinding bindingConfiguration) {// input check if (targetElement == null)return;if (sourceElement == null)return;if (bindingConfiguration == null)return; // check binding configuration// ...target property must be set if (bindingConfiguration.TargetProperty.IsNullOrWhiteSpace())return;// ...path property must be set if (bindingConfiguration.Path.IsNullOrWhiteSpace())return; // support of attached property binding syntax: TargetProperty='(Grid.Row)' string targetPropertyName = (bindingConfiguration.TargetProperty + "").Trim().TrimStart('(').TrimEnd(')') + "Property"; // find the target dependency property DependencyProperty targetDependencyProperty = null;if (targetPropertyName.Contains(".")) {// it is an attached dependency property string[] parts = targetPropertyName.Split('.'); if (parts.Length == 2 && !parts[0].IsNullOrWhiteSpace() && !parts[1].IsNullOrWhiteSpace()) { Type attachedType = TypeLoader.GetType(parts[0], bindingConfiguration.TargetNamespace); if (attachedType != null) { FieldInfo[] targetFields = attachedType.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); FieldInfo targetDependencyPropertyField = targetFields.FirstOrDefault(i => i.Name == parts[1]);if (targetDependencyPropertyField != null) targetDependencyProperty = targetDependencyPropertyField.GetValue(null) as DependencyProperty; } } }else {// it is a standard dependency property FieldInfo[] targetFields = targetElement.GetType().GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); FieldInfo targetDependencyPropertyField = targetFields.FirstOrDefault(i => i.Name == targetPropertyName); if (targetDependencyPropertyField != null) targetDependencyProperty = targetDependencyPropertyField.GetValue(null) as DependencyProperty; } // set binding if (targetDependencyProperty != null) { if (bindingConfiguration.TargetDependencyProperty != null&& bindingConfiguration.TargetDependencyProperty != targetDependencyProperty) targetElement.ClearValue(bindingConfiguration.TargetDependencyProperty); bindingConfiguration.TargetDependencyProperty = targetDependencyProperty; Binding binding = new Binding(); binding.Source = sourceElement; binding.Path = new PropertyPath(bindingConfiguration.Path); binding.Mode = bindingConfiguration.BindingMode; binding.Converter = bindingConfiguration.Converter; binding.ConverterParameter = bindingConfiguration.ConverterParameter; binding.ConverterCulture = bindingConfiguration.ConverterCulture; binding.NotifyOnValidationError = bindingConfiguration.NotifyOnValidationError;#if !WINDOWS_PHONE binding.ValidatesOnDataErrors = bindingConfiguration.ValidatesOnDataErrors;#endif binding.ValidatesOnExceptions = bindingConfiguration.ValidatesOnExceptions;#if !WINDOWS_PHONE binding.ValidatesOnNotifyDataErrors = bindingConfiguration.ValidatesOnNotifyDataErrors;#endif// set the binding on our target element targetElement.SetBinding(targetDependencyProperty, binding); } } #endregion }
也把这个XMAL类型分析的小工具代码转给大家吧
/// <summary>/// Provides functionality to load any type with its class name, namespace and assembly-name within the Silverlight environment./// </summary>/// <remarks>/// The Type.GetType method is different in Silverlight than in the standard .NET runtime. In Silverlight we have to provide the /// fully qualified assembly name to get a type in a custom assembly. Only build in controls or types in the same assembly are /// excluded from this rule. Full qualified assembly name means a syntax like the following: /// MyComponent.MyType, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4bec85d7bec6698f./// This class uses the XamlReader capability to resolve type during parsing a xaml-string. While this is a little time consuming/// the TypeLoader maintains a cache to get types faster./// </remarks> public static class TypeLoader {// cache for resolved type private static Dictionary<string, Type> _cache = new Dictionary<string, Type>(); /// <summary>/// Gets the System.Type with the specified name, name space and assembly name./// </summary>/// <param name="className">The class name without namespace.</param>/// <param name="nameSpace">The name space</param>/// <param name="assemblyName">The name of the assembly containing the type.</param>/// <returns>The type matching the provided parameters or null if not found.</returns> //[DebuggerStepThrough()] public static Type GetType(string className, string nameSpace, string assemblyName) {// check if (nameSpace.IsNullOrWhiteSpace())return null; string xamlNamespace = string.Format("clr-namespace:{0}", nameSpace);// assembly name is optional if (!assemblyName.IsNullOrWhiteSpace()) xamlNamespace += string.Format(";assembly={0}", assemblyName); return GetType(className, xamlNamespace); } /// <summary>/// Gets the System.Type with the specified name. /// This method overload can be used for:/// 1. core controls such as Button, Grid, ListBox, etc. without specifying the namespace or assembly name./// 2. with the qualified assembly name of the type without version and public key token like this: "MyNamespace.MyType, MyAssembly"./// </summary>/// <param name="className">Pure class name of Core Controls such as Button, Grid, ListBox, etc.</param>/// <returns>The type matching the provided parameters or null if not found.</returns> //[DebuggerStepThrough()] public static Type GetType(string className) {if (className != null && className.Contains(",")) {string[] qualifiedNameParts = className.Split(','); if (qualifiedNameParts.Length == 2) {string[] fullClassNameParts = qualifiedNameParts[0].Split('.'); if (fullClassNameParts.Length > 0) {// classname string newClassName = fullClassNameParts.Last().Trim(); // namespace string nameSpace = "";for (int i = 0; i < fullClassNameParts.Length - 1; i++) { nameSpace += fullClassNameParts[i] + "."; } nameSpace = nameSpace.TrimEnd('.'); string assemblyName = qualifiedNameParts[1].Trim(); return GetType(newClassName, nameSpace, assemblyName); } } } return GetType(className, ""); } /// <summary>/// Gets the System.Type with the specified name. The xaml namespace specifies the namespace and assembly name in the same syntax as in xaml. /// </summary>/// <param name="className">The class name without namespace.</param>/// <param name="xamlNamespace">/// The xaml namespace. This is the same syntax as used in XAML syntax. /// Example: "clr-namespace:MyComponent.SubNamespace;assembly=MyAssemblyName/// </param>/// <returns>The type matching the provided parameters or null if not found.</returns> //[DebuggerStepThrough()] public static Type GetType(string className, string xamlNamespace) {// check input if (className.IsNullOrWhiteSpace())return null; if (className.Contains("."))throw new ArgumentException("className must not include the namespace. Please provide namespace with separate parameter."); // check if type is already in cache string key = xamlNamespace + "&" + className; if (_cache.ContainsKey(key))return _cache[key]; lock (_cache) {try {// check again because another thread might be faster and has already created the cache-entry if (_cache.ContainsKey(key))return _cache[key]; // create xaml with a simply Style element and set the TargetType property with the provided type name string xaml = "<Style xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' "; // set the xaml namesapce if provided if (!xamlNamespace.IsNullOrWhiteSpace()) { xaml += string.Format("xmlns:tmp='{0}' TargetType='tmp:{1}' />", xamlNamespace, className); }else {// Core controls such as Button, Grid, ListBox, etc do not need a namespace xaml += string.Format("TargetType='{0}' />", className); } // let the XamlParser load the type via the TargetType property Style style = XamlReader.Load(xaml) as Style; if (style != null) { Type targetType = style.TargetType; AddToCache(key, targetType);return targetType; } }catch (Exception ex) {// Try to load type in executing assembly if (!xamlNamespace.IsNullOrWhiteSpace()) {// note: Type.GetType uses needs assembly-qualified name of the type to get. If the type is // in the currently executing assembly or in Mscorlib.dll, it is sufficient to supply // the type name qualified by its namespace. Type type = Type.GetType(string.Format("{0}.{1}", xamlNamespace.Replace("clr-namespace:", "").TrimEnd(';'), className)); if (type != null) {// add to cache AddToCache(key, type);return type; } } //****** DONT SET VALUE TO NULL, BECAUSE OF CASES WHEN AN ASSEMBLY IS *****//****** LOADED DYNAMICALLY INTO THE APPLICATION DOMAIN *****// don't let the exception repeat. Set null as cache value AddToCache(key, null);//**************************************************************************/ } } return null; } private static void AddToCache(string key, Type type) { _cache.Add(key, type); CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering); CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering); } static void CompositionTarget_Rendering(object sender, EventArgs e) { CompositionTarget.Rendering -= new EventHandler(CompositionTarget_Rendering); _cache.Clear(); } }
至此快成功了试一下:
<my:CommandButton CommandParameter="{Binding}" >
<localBinding:BindingAdapter.Binding>
<localBinding:RelativeSourceBinding Path="ClickCommmand" TargetProperty="Command"
RelativeMode="ParentDataContext" />
</localBinding:BindingAdapter.Binding>
......................
<my:CommandButton CommandParameter="{Binding}" >
<localBinding:BindingAdapter.Binding>
<localBinding:RelativeSourceBinding Path="DataContext.ClickCommmand" TargetProperty="Command"
RelativeMode="FindAncestor" AncestorType="Grid" />
</localBinding:BindingAdapter.Binding>
.............
<ComboBox Grid.Column="1"><local:BindingAdapter.Binding><local:BindingList><local:RelativeSourceBinding Path="DataContext.Picklist" TargetProperty="ItemsSource" RelativeMode="FindAncestor" AncestorType="UserControl" /><local:RelativeSourceBinding Path="DataContext.Tooltip"TargetProperty="(ToolTipService.ToolTip)" RelativeMode="FindAncestor"AncestorType="UserControl" /><local:RelativeSourceBinding Path="DataContext.Tooltip" TargetProperty="(DemoAttachedElement.Value)"TargetNamespace="clr-namespace:********;assembly=*******" RelativeMode="FindAncestor" AncestorType="UserControl" /></local:BindingList></local:BindingAdapter.Binding></ComboBox>
转载于:https://www.cnblogs.com/zhouhoujun/archive/2011/07/18/2109067.html
在WP7下自定义RelativeSource 的Binding相关推荐
- opensuse-KDE桌面下自定义快捷键,ctrl+alt+t打开konsole
2019独角兽企业重金招聘Python工程师标准>>> opensuse-KDE桌面下自定义快捷键,ctrl+alt+t打开konsole 转载于:https://my.oschin ...
- 异贝,通过移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例62
欢迎关注异贝.异贝5G营销工具,今天给大家带来的是体育用品店的营销案例: 生命不止,运动不息.许多人都爱运动,爱体育,但随着社会发展,越来越少人会去到传统的体育用品店购买运动用品.一方面原因是电商的发 ...
- 异贝,通过移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例52
欢迎关注异贝.异贝5G营销工具,今天给大家带来的案例是小超市的营销方案: 随着经济的不断发展,越来越多的大型企业都看中了四五线县城的人口和消费能力,许多深耕当地的经营场所,都收到了这些"外来 ...
- 异贝,通过移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例53
欢迎关注异贝.异贝5G营销工具,今天给大家带来的案例是桌游店的营销方案: 随着娱乐至上的时代来临,各式各样的游戏.项目层出不穷,而其中最火热的闲暇娱乐项目,要数因手游而孕育而生的"剧本杀&q ...
- 异贝5G新零售工具,通过移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案的推送。
异贝通过移动互联网技术应用,为中国实体企业实现企业联盟.线上链接.线上线下自定义营销方案的推送.实体企业互联网平台搭建:各种系统研发及技术智慧输出的一站式营销孵化综合服务科技平台! 经过团队的努力与发 ...
- 异贝,移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例42
欢迎关注异贝.异贝5G营销工具,为实体商家科技赋能,今天给大家带来的案例是羊毛衫店的营销: 人天生就有爱占便宜的本性,这在消费者心理学中被称作合算偏见,人们在交易的时候,不仅要买商品,也要买商品时占到 ...
- 异贝,通过移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例69
欢迎关注异贝.异贝5G营销工具.今天给大家带来的是服装店的营销案例: 今天分享的案例是一位浙江的美女所开的一家服装店,在服装店刚开始营业的那段时间里,由于靠着父母的资源和关系,小朱的服装店在当地服装圈 ...
- 异贝,通过移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例60
欢迎关注异贝.异贝5G营销工具,今天给大家带来的案例是儿童玩具市场的营销案例: 现在的儿童玩具市场,既有越来越旺盛的趋势,却也同样面临着的激烈的竞争.除了原来卖玩具的同行的竞争,还有各个教育机构也在出 ...
- 异贝,通过移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例55
欢迎关注异贝.异贝5G营销工具,今天给大家带来的案例是ktv的营销方案: 现在很多的KTV基本上都会上美团.大众等团购网以低价,甚至超低价来吸引顾客,然后会以酒水.零食.小吃.拼盘等赚钱,说白了就是先 ...
最新文章
- Protoc Buffer 优化传输大小的一个细节
- 机器学习笔记:岭回归(L2正则化)
- MySQL-binlog格式对主从复制的影响MySQL主从复制的过程
- 深究AngularJS——校验(非form表单)
- Keras Data augmentation(数据扩充)
- 强化学习原理与python实现原理pdf_纯Python实现!Facebook发布PyTorch分布式强化学习库...
- 成交量与股价的关系图解
- 80-10-010-原理-Java NIO-简介
- python预测实例教程_手把手教你用Python库Keras做预测(附代码)-阿里云开发者社区...
- Code4Fun: 通过XML模板系统实现对象的灵活序列化
- 10 LVS负载均衡群集-NAT
- 爱心函数可视化 python
- html标签img是什么意思,html中img标签属性是什么意思
- Java爬虫彼岸桌面壁纸(使用httpClient+Jsoup)
- c语言建立并存储树,利用十字链表存储树结构(便于同时求出某一点的入度与出度)------C语言实现...
- kettle用命令行执行ktr和kjb
- java 学生兼职_javaweb大学生兼职平台
- 计算机引起usb设备无法识别的原因有哪些,计算机无法识别USB设备是什么原因
- sql 语句中count()条件计数
- 【C#】WPF的xaml中定义的Trigger为什么有时候会不管用,如Border的MouseOver之类的
热门文章
- php多表头表格,HTML多表头表格代码示例
- mysql mysql_row 整行数据_PHP使用mysql_fetch_row查询获得数据行列表的方法,phpmysql_fetch_row_PHP教程...
- matlab画三维图
- zynq学习02 新建一个Helloworld工程
- php采集列表xml代码,php读取xml列表程序
- python bar
- RUST等差分解一个数
- 突然想到一个可以减少fc层权重数的方法
- pandas版xml json excel互转
- 光伏行业需理性看待低价中标 市场竞争是必然选择